题意:两个人玩游戏,初始集合S为大于1的正整数集,轮流写出一个大于1的数然后将:“1、所有这个数的倍数;2、这个数的倍数与前面已经删去的数的和”从集合S中删去,现在告诉你S的现状,求所有必胜走法的第一步。
题解:数的数量小于20,可以用位压缩dp,记录S中还剩哪些元素时是否是必胜态,然后通过记忆化搜索求出所有走第一步后是必败态的策略。
View Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int dp[(1<<20)+2];//1:胜;0:负 6 int a[25],n; 7 bool mark[25]; 8 int update(int state,int k) 9 { 10 int st=state^(1<<k),p=a[k],t; 11 mark[p]=false; 12 for(int i=0;i<n;i++) 13 { 14 if(st&(1<<i)) 15 { 16 t=a[i]-p; 17 if(t>1&&!mark[t]) 18 { 19 mark[a[i]]=false; 20 st^=(1<<i); 21 } 22 } 23 } 24 return st; 25 } 26 void resume(int state,int org) 27 { 28 for(int i=0;i<n;i++) 29 { 30 int t=1<<i; 31 if(!(state&t)&&(org&t)) 32 mark[a[i]]=true; 33 } 34 } 35 int dfs(int state) 36 { 37 if(dp[state]!=-1) 38 return dp[state]; 39 for(int i=0;i<n;i++) 40 { 41 if(state&(1<<i)) 42 { 43 int st=update(state,i); 44 if(dfs(st)==0) 45 { 46 resume(st,state); 47 return dp[state]=1; 48 } 49 resume(st,state); 50 } 51 } 52 return dp[state]=0; 53 } 54 int main() 55 { 56 int ca=0; 57 while(scanf("%d",&n),n) 58 { 59 int state=0; 60 memset(mark,false,sizeof(mark)); 61 memset(dp,-1,sizeof(dp)); 62 dp[0]=0; 63 for(int i=0;i<n;i++) 64 scanf("%d",&a[i]),mark[a[i]]=true,state|=(1<<i); 65 sort(a,a+n); 66 int top=0,ans[25]; 67 for(int i=0;i<n;i++) 68 { 69 int st=update(state,i); 70 if(dfs(st)==0) 71 ans[top++]=a[i]; 72 resume(st,state); 73 } 74 printf("Test Case #%d\n",++ca); 75 if(top==0) 76 printf("There's no winning move.\n\n"); 77 else 78 { 79 printf("The winning moves are:"); 80 for(int i=0;i<top;i++) 81 printf(" %d",ans[i]); 82 printf("\n\n"); 83 } 84 } 85 return 0; 86 }