题意:两个人玩游戏,初始集合S为大于1的正整数集,轮流写出一个大于1的数然后将:“1、所有这个数的倍数;2、这个数的倍数与前面已经删去的数的和”从集合S中删去,现在告诉你S的现状,求所有必胜走法的第一步。
题解:数的数量小于20,可以用位压缩dp,记录S中还剩哪些元素时是否是必胜态,然后通过记忆化搜索求出所有走第一步后是必败态的策略。
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 }