A Simple Nim

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 195    Accepted Submission(s): 133


Problem Description
Two players take turns picking candies from n heaps,the player who picks the last one will win the game.On each turn they can pick any number of candies which come from the same heap(picking no candy is not allowed).To make the game more interesting,players can separate one heap into three smaller heaps(no empty heaps)instead of the picking operation.Please find out which player will win the game if each of them never make mistakes.
 

Input
Intput contains multiple test cases. The first line is an integer 1T100, the number of test cases. Each case begins with an integer n, indicating the number of the heaps, the next line contains N integers s[0],s[1],....,s[n1]          , representing heaps with  s[0],s[1],...,s[n1]          objects  respectively. (1n106,1s[i]109)
 

Output
For each test case,output a line whick contains either"First player wins."or"Second player wins".
 

Sample Input
2 2 4 4 3 1 2 4
 

Sample Output
Second player wins. First player wins.
 

Author
UESTC
 

Source

2016 Multi-University Training Contest 6 


思路:

这道题是一道典型的博弈论sg问题。

sg问题在这里有详解----->传送门

因为这个题数量级的范围是10的9次方级,所以没办法一个一个去求sg值,所以只能能过sg打表的形式来找出sg变化的规律,然后通过数学归纳总结出sg和x的一个关系式。再根据打表可以看出来规律,每当x到达8的倍数的时候,sg的值就等于x-1,如果x整除8等于7的时候,sg值就等于x+1,其他情况sg就等于x。然后就找到了sg与x之间的规律,那么就能推导所有的x与其所对应的sg之间的表达式。

至于sg值打表,先把vis数组全都初始化为0,然后遍历每个数的sg值vis[sg值出现过的数]都变为1,代表取石子的过程。然后将所有能拆分的都拆分,即用“^”来拆分sg值。然后当出现sg为0的时候,把其所作为sg的值所对应的那个第几个sg记录下来。所得sg打表如图:


然后就可以得到上面的规律啦。


代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //sg值打表程序  
  2. #include <iostream>  
  3. #include <cstdio>  
  4. #include <cstring>  
  5. #include <algorithm>  
  6. #include <cstdlib>  
  7. using namespace std;  
  8.   
  9. int sg[32];  
  10. bool vis[32];  
  11. int a[3];  
  12.   
  13. int main()  
  14. {  
  15.     for (int i=1; i<32; i++) {  
  16.         memset(vis,0,sizeof(vis));  
  17.         int j,k;  
  18.         for (j=0; j<i; j++) vis[sg[j]]=1;  
  19.         for (j=1; j<i; j++)  
  20.             for (k=1; j+k<i; k++)  
  21.                 vis[sg[k]^sg[j]^sg[(i-k-j)]]=1;  
  22.         for (j=0; j<32&&vis[j]; j++);  
  23.         sg[i]=j;  
  24.     }  
  25.     for (int i=1; i<32; i++)  
  26.         printf("%d %d\n",i,sg[i]);  
  27.     return 0;  
  28. }  

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //主程序   
  2. #include <cstdio>    
  3. #include <cstring>    
  4. #include <iostream>    
  5. #include <algorithm>    
  6. using namespace std;    
  7.     
  8. int find_sg(int x)    
  9. {    
  10.     if(x%8==0)return x-1;    
  11.     else if(x%8==7)return x+1;    
  12.     return x;    
  13. }    
  14. int main()    
  15. {    
  16.     int T;    
  17.     scanf("%d",&T);    
  18.     while(T--)    
  19.     {    
  20.         int a,n,i,j,ans=0;    
  21.         scanf("%d",&n);    
  22.         for(i=0;i<n;i++)    
  23.         {    
  24.             scanf("%d",&a);    
  25.             ans=ans^find_sg(a);    
  26.         }    
  27.         if(ans==0)printf("Second player wins.\n");    
  28.         else printf("First player wins.\n");    
  29.     }    
  30.     return 0;    
  31. }      

收藏