题目链接:UVALive - 3668
题目大意为给定n堆石子,每次的操作是选择三个数i<j<=k,从i中拿一枚石子,在j和k中分别放入一枚石子。不能操作者输。求先手是否能赢,若可以,则输出字典序最小的第一步操作。
思路是把在每个位置上的每颗石子当成一个游戏。
用SG[i]表示在第i堆中的一颗石子的sg函数。
则SG[i]=mex(SG[j] ^ SG[k])。
然后异或求游戏的和即可。
为找到字典序最小的第一步操作,我们枚举第一步操作,然后求游戏的和即可。
代码如下:
1 #include"cstdio" 2 #include"iostream" 3 #include"cstring" 4 #include"algorithm" 5 #include"cstdlib" 6 #include"vector" 7 #include"set" 8 #include"map" 9 #include"cmath" 10 using namespace std; 11 typedef long long LL; 12 const LL MAXN=30; 13 14 int n; 15 int f[MAXN]; 16 int sg(int x) 17 { 18 if(x==n) return 0; 19 if(f[x]!=-1) return f[x]; 20 int vis[200]; 21 memset(vis,0,sizeof(vis)); 22 for(int i=x+1;i<=n;i++) 23 for(int j=i;j<=n;j++) 24 vis[sg(i)^sg(j)]=1; 25 for(int i=0;i<200;i++) 26 if(!vis[i]) 27 return f[x]=i; 28 return 200; 29 } 30 int d[MAXN]; 31 int cal() 32 { 33 memset(f,-1,sizeof(f)); 34 int ans=0; 35 for(int i=1;i<=n;i++) 36 if(d[i]%2!=0) 37 ans ^= sg(i); 38 return ans; 39 } 40 int main() 41 { 42 #ifdef LOCAL 43 freopen("in.txt","r",stdin); 44 // freopen("out.txt","w",stdout); 45 #endif 46 int t=0; 47 while(scanf("%d",&n)!=EOF && n) 48 { 49 for(int i=1;i<=n;i++) 50 scanf("%d",&d[i]); 51 int i,j,k; 52 bool ok=false; 53 for(i=1;!ok && i<=n;i++) 54 for(j=i+1;!ok && j<=n;j++) 55 for(k=j;!ok && k<=n;k++) 56 if(d[i]>0) 57 { 58 d[i]--; 59 d[j]++; 60 d[k]++; 61 if(cal()==0) 62 ok=true; 63 d[i]++; 64 d[j]--; 65 d[k]--; 66 } 67 printf("Game %d: ",++t); 68 if(ok) printf("%d %d %d ",i-2,j-2,k-2); 69 else printf("-1 -1 -1 "); 70 } 71 return 0; 72 }