HDOJ 5795 A Simple Nim (博弈Nim+sg函数)

本文详细解析了一道典型的博弈论SG问题,通过分析题目特点,采用SG值打表的方法找到规律,并给出了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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. }      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值