首页 > 代码库 > hdu4876
hdu4876
题意:有n(<=20)个数,在n个数中挑出k(<=6)个数组成一个环。给出一个L,这个环中的数(可以是1个,2个....k个)异或操作(只能是连续的几个数异或)会得到一系列的值,求这些值包含L并且向L+1,L+2延伸,所能取到的最大R。(L--R之间所有的数都必须取得到)
基础思路:就是先取出k个数,然后对这k个数进行全排列,在去找最大的R--这样必然超时。
标程给出的思路:先取k个数,最多有C(20,6)种取法,但是这些取法中有绝大部分是不符合要求的,这样,可以先不进行全排列,先对这6个数进行异或操作,把所有可以取得到的抑或值全部取出来,然后求出它最长的连续的包含L的r,然后与最终的结果R比较(R一开始等于L-1),如果这样取得到的r还<=R,那么这一组取法肯定是不行的。
如果r>R,那么就对这取得的k个数进行全排列,这里需要注意,在全排列的时候,由于k个数组成一个圆,那么只要对第一个数之后的k-1个数进行全排列就好,如果不这样,那么超时无疑。
然后找出一个符合条件的r,如果这个r大于R,那么我们需要更新R(也就是一边搜索,一遍把已经搜索到的结果拿来剪枝)。
我自己按照标程给出的思路编写的时候,TLE\WA了一整天,弄得都快崩溃了--最后还是ac了,然后发现是自己sb了。。。。。
反思:在进行取数的时候,在比赛时,我是用dfs回溯写的,时间复杂度那叫一个大,是队友提醒不用回溯的......这里也2b了,话说不就是取与不取的关系么?回溯干嘛呢?
在写取连续的值的时候,我是少取了一些数,虽然测出来了,但是这不是应该出现的错误,一个圈的话,那不只是取到最后一个数就可以了,要取完一个圈。
ac代码:
#include<iostream>#include<cstdio>#include<cstring>#include<vector>using namespace std;const int inf=300;int n,m,ll,maxn;int s[inf];int tol[53259][8],f[8],cnt=0;bool vist[inf],p[inf];int xx[17500][8],cntx=0;bool zt[(2<<8)];void dfs(int num,int step) //取k个数,C(n,k){ if(step==m) { for(int i=0; i<m; i++) tol[cnt][i]=f[i]; cnt++; return; } if(num>n) return; dfs(num+1,step); //取,或者不取,吐槽一下,为什么我总喜欢写 f[step]=s[num]; //for循环?这绝对是做记忆化搜索留下的 dfs(num+1,step+1);}void dfs1(int wz,int num,int ans) //标记k个数,所有能取到的异或值{ p[ans]=true; if(wz==m) return; if(wz>m) return; dfs1(wz+1,num,ans); dfs1(wz+1,num,ans^tol[num][wz]);}void dfs2(int num,int step) //求全排列,这里是绝对要回溯的{ if(step>0&&!vist[0]) return; //这样是表示,第一个数是固定的 if(step==m) //由于是一个圈,所以,只需要求出 { //后面k-1个数的全排列就好 for(int i=0;i<m;i++) xx[cntx][i]=f[i]; cntx++; return; } for(int i=0;i<m;i++) { if(!vist[i]) { vist[i]=true; f[step]=tol[num][i]; dfs2(num,step+1); vist[i]=false; } }}void deal(int num) //这是求连续的抑或值{ int ans=0; for(int i=0;i<m;i++) { int ans=xx[num][i]; vist[ans]=true; int j=1,h=i+j; while(1) //一开始这里2b了,写成了for(j=i+1;j<m;j++),少取了很多数据 { if(j==m) break; ans^=xx[num][h%m]; vist[ans]=true; j++; h=i+j; } }}int main(){ while(scanf("%d%d%d",&n,&m,&ll)>0) { for(int i=1;i<=n;i++) scanf("%d",&s[i]); cnt=0; dfs(1,0); //printf("dfs\n"); int rr=ll-1; //初始化rr,一开始应该等于ll-1 for(int i=0;i<cnt;i++) { memset(p,false,sizeof(p)); maxn=ll-1; dfs1(0,i,0); memset(vist,false,sizeof(vist)); for(int j=ll;j<=150;j++) { if(p[j]) maxn++; else break; } if(maxn<=rr) continue; //剪枝,rr表示最终的结果 cntx=0; dfs2(i,0); for(int j=0;j<cntx;j++) { int right=ll-1; memset(vist,false,sizeof(vist)); deal(j); for(int k=ll;k<=150;k++) { if(vist[k]) right++; else break; } if(rr<right) rr=right; //更新rr值 } } if(rr<ll) printf("0\n"); else printf("%d\n",rr); } return 0;}