NOIP2015提高组Day1
来源:互联网 发布:淘宝毕业论文多少钱 编辑:程序博客网 时间:2024/05/19 23:52
一、神奇的幻方
数据:对于100%,n∈[1,39],且n必为奇数
模拟题。没什么好讲的。
重点是,noip2017的提高组初赛是这道题……
二、信息传递
数据:对于60%,n∈[1,2500]
对于100%,n∈[1,200000]
题目里给的序列可以变成一张图,根据题目意思在自己画一个有向图,发现就是求一个图上的环的长度。而且只有n条边,那么只有一个环。如果学了强联通,那么就是裸题了。但是不会,只能换方法。
因为只有一个环,环是连通的,想想以前的压缩路径。并查集,把每个点都压起来,压起来后如果找到的父亲是一样的。那么就是说环变成了点,那么此时再dfs就行了。
这个比暴力的快就是在判断什么时候dfs。暴力是每个点都dfs,而这个只dfs一次。这样就从n^2变到n了。
#include<bits/stdc++.h>#define M 200005using namespace std;int n,A[M],ans=1e9,H[M],Fa[M];int Find(int x){return Fa[x]==x?x:Fa[x]=Find(Fa[x]);}bool Q[M];void f(int x,int t){//dfs Q[x]=1;H[x]=t; if(Q[A[x]]==0)f(A[x],t+1); else ans=min(ans,H[x]-H[A[x]]+1);}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&A[i]); for(int i=1;i<=n;i++)Fa[i]=i; for(int i=1;i<=n;i++){ if(Find(i)!=Find(A[i])){//当父亲不相同的时候,就一直压缩 int x=Find(i),y=Find(A[i]); Fa[x]=y; }else{ memset(Q,0,sizeof Q); f(i,1); } } printf("%d\n",ans); return 0;}
三、斗地主
数据:
考试肯定先打暴力。前30分if语句就足够了。
然后写法很多:深搜写得好有100分(我一分没有);广搜有80分;dp的预处理加深搜有100分。
讲讲dp的预处理加深搜。
其实很好理解。如果没有顺子这种情况,那么现在手上的牌的大小和花色都没有任何影响。唯一有影响的只是每种牌的张数。那么就可以先预处理。dp[i][j][k][l] 表示当有i个一张,j个两张,k个三张,l个四张时打完要的次数(不包括顺子的情况)。转移也很简单,只是情况有点多。对于一个dp[i][j][k][l],当满足条件的时候(即某种张数的牌有的时候),可以合并一些,如四代二,三代一,三代二之类的。还可以把四张的分成三张加一张,两张加两张(为了组合其他的可能性),三张变成两张加一张,两张编织成一张加一张。
总的来说dp的转移分成三大类:
1、最初始的情况,多一个一、二、三、四张,往前要值
2、合并一些,3+2,3+1,4+1+1,4+2+2
3、拆分一些,2=1+1,3=2+1,4=3+1,4=2+2
预处理好了就是深搜的事了。既然除了顺子的都已经处理好了,那么只要考虑各种顺子的情况就行了。顺子分成单张、两张和三张三种情况,分类一下就行了,相比一开始的暴力深搜,这简单多了。
Code:
#include<bits/stdc++.h>#define M 25using namespace std;int cas,n,dp[25][25][25][25],cnt[20],ans,Cnt[10];void Init(){ memset(dp,63,sizeof dp); dp[0][0][0][0]=0; for(int l=0;l<=n;l++) for(int k=0;k<=n;k++) if(l*4+k*3<=n)for(int j=0;j<=n;j++) if(l*4+k*3+j*2<=n)for(int i=0;i<=n;i++) if(l*4+k*3+j*2+i<=n){ //第一类,多一张,向以前要值 if(i)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k][l]+1); if(j)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k][l]+1); if(k)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k-1][l]+1); if(l)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k][l-1]+1); //第二类,合并 if(l>=2)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k][l-2]+1);//4+4,特殊情况,炸弹看成两个对子 if(k&&i)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k-1][l]+1);//3+1 if(k&&j)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k-1][l]+1);//3+2 if(l&&i>=2)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-2][j][k][l-1]+1);//4+2+2 if(l&&j>=2)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-2][k][l-1]+1);//4+1+1 if(l&&j)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k][l-1]+1);//4+1+1,特殊情况,对子看成两个单张 //第三类,拆分 if(j)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i+2][j-1][k][l]);//2=1+1 if(k)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i+1][j+1][k-1][l]);//3=2+1 if(l)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i+1][j][k+1][l-1]);//4=3+1 if(l)dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j+2][k][l-1]);//4=2+2 }}void f(int res){//res是现在的各类顺子的个数 if(res>ans)return;//最优性剪枝 memset(Cnt,0,sizeof Cnt); for(int i=0;i<=14;i++)Cnt[cnt[i]]++;//Cnt记录现在有多少组一二三四的牌 ans=min(ans,dp[Cnt[1]][Cnt[2]][Cnt[3]][Cnt[4]]+res); for(int i=3;i<=14;i++){ for(int j=5;j<=13;j++){//单顺子 int p1=1; for(int k=i;k<=i+j-1;k++)if(cnt[k]<1)p1=0; if(!p1)break; for(int k=i;k<=i+j-1;k++)cnt[k]--; f(res+1); for(int k=i;k<=i+j-1;k++)cnt[k]++; } for(int j=3;j<=13;j++){//双顺子 int p1=1; for(int k=i;k<=i+j-1;k++)if(cnt[k]<2)p1=0; if(!p1)break; for(int k=i;k<=i+j-1;k++)cnt[k]-=2; f(res+1); for(int k=i;k<=i+j-1;k++)cnt[k]+=2; } for(int j=2;j<=13;j++){//三顺子 int p1=1; for(int k=i;k<=i+j-1;k++)if(cnt[k]<3)p1=0; if(!p1)break; for(int k=i;k<=i+j-1;k++)cnt[k]-=3; f(res+1); for(int k=i;k<=i+j-1;k++)cnt[k]+=3; } }}int main(){ scanf("%d%d",&cas,&n); Init(); while(cas--){ memset(cnt,0,sizeof cnt); for(int i=1;i<=n;i++){ int x,y; scanf("%d%d",&x,&y); if(x==1)cnt[14]++; else cnt[x]++; } ans=n; f(0); printf("%d\n",ans); } return 0;}
其实还好,只是考试的时候一般是想不出来的。思路理清楚了还是可以的。
对于第二题还有一个,对于所有图、树的题,弗洛伊德肯定要打,暴力但是准确。
- NOIP2015提高组Day1
- {小结}NOIP2015提高组Day1
- NOIP2015提高组Day1 Message
- NOIP2015 提高组 day1 信息传递
- NOIP2015提高组Day1斗地主
- 【NOIP2015提高组Day1】信息传递
- NOIP2015 提高组 day1 信息传递
- 【jzoj4325】【NOIP2015提高组Day1】【斗地主】
- 信息传递 NOIP2015 提高组 Day1 T2
- JZOJsenior4324.【NOIP2015提高组Day1】信息传递
- XJOI-NOIP2015提高组模拟题1 day1
- NOIP2015 提高组 day1 神奇的幻方
- NOIP2015提高组day1 —— 信息传递(message)
- Noip2015提高组Day1 “神奇的幻方”题解
- 【NOIP2015提高组Day1】 神奇的幻方
- NOIP2015 提高组 day1 神奇的幻方
- NOIP2015 提高组 复赛 day1 magic 神奇的幻方
- NOIP2015 提高组 复赛 day1 message 信息传递
- android 用OPENGL ES 实现投影效果(正交投影与透视投影)
- 【NOI2001】炮兵阵地(状态压缩,动态规划)
- Pyhton注释符号使用方法及规范
- Unity3D设计模式之命令模式
- mybatis知识点
- NOIP2015提高组Day1
- 笔记本电脑通用的bios界面图文详解大全
- Python在Windows下使用ez_setup.py安装pip
- 树莓派设置中文显示
- 支付宝单笔转账实现
- matlab中exp()函数的使用
- supervisor配置使用
- Python调用imsave报错ImportError: cannot import name imsave
- StringBuffer类复习