【题目】
Description The funny stone game is coming. There are n piles of stones, numbered with 0, 1, 2, ..., n − 1. Two Input Sample Input |
【题目翻译】
David 玩一个石子游戏。游戏中,有n堆石子,被编号为0..n-1。两名玩家轮流取石子。每一轮游戏,每名玩家选取3堆石子i,j,k(i<j,j<=k,且至少有一枚石子在第i堆石子中),从i中取出一枚石子,并向j,k中各放入一枚石子(如果j=k则向k中放入2颗石子)。最先不能取石子的人输。
请编程帮助David。
石子堆的个数不会超过23,每一堆石子不超过1000个。
【分析】
首先,假设第i堆有xi个石子,那么可以先把xi%2。
因为在同一堆中的两颗石子是一模一样的。对方对这颗石子做什么,你就可以对另外一颗石子做同样的事,相当于这两颗一样的石子不存在。
因为每颗石子可以变成两颗放到后面去,也就是说它的转移状态和它的编号有关。
可以将每一颗石子看作是一堆石子,如果它是第p堆中的石子,把么它所代表的这堆石子的个数为n-1-p。
因为石子堆是互不干扰的,因此这个游戏可以看作由若干个只有一堆石子的游戏组成。(把其单独考虑开来)
求它能到达的子状态的尼姆和更新自己的sg值即可。跟扫楼梯一题差不多,即使这堆石子的个数为偶数个,他可能还是有用的,即可以拆分也把状态改变为平衡状态的,要把这个也考虑上。(好像说得不是很清楚,具体看代码吧~~)
代码如下:(看错数据范围了,懒得改了,就酱吧~)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 1010 9 10 int n; 11 int a[2*Maxn],b[2*Maxn],sg[2*Maxn]; 12 bool vis[2*Maxn]; 13 14 void get_sg(int x) 15 { 16 memset(vis,0,sizeof(vis)); 17 for(int i=1;i<x;i++) 18 for(int j=i;j<x;j++) 19 { 20 vis[sg[i]^sg[j]]=1; 21 } 22 for(int i=0;i<=2000;i++) 23 if(vis[i]==0) {sg[x]=i;break;} 24 } 25 26 bool output(int x,int now) 27 { 28 for(int i=x-1;i>=1;i--) 29 for(int j=i;j>=1;j--) 30 if((sg[i]^sg[j])==now) 31 { 32 printf("%d %d %d\n",n-x,n-i,n-j); 33 return 1; 34 } 35 return 0; 36 } 37 38 int main() 39 { 40 int kase=0; 41 for(int i=1;i<=1000;i++) get_sg(i); 42 while(1) 43 { 44 scanf("%d",&n); 45 if(n==0) break; 46 int ans=0; 47 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 48 for(int i=1;i<=n;i++) b[i]=a[n-i+1]; 49 for(int i=1;i<=n;i++) 50 { 51 if(b[i]%2==1) ans^=sg[i]; 52 } 53 printf("Game %d: ",++kase); 54 if(ans==0) {printf("-1 -1 -1\n");continue;} 55 int mx=0; 56 for(int i=0;(1<<i)<=ans;i++) 57 if((1<<i)&ans) mx=(1<<i); 58 for(int i=n;i>=1;i--) 59 if(b[i]!=0) {if(output(i,ans^sg[i])) break;} 60 } 61 return 0; 62 }
[UVA1378]
2016-04-17 17:12:38