NIMK博弈

普通的NIM游戏是在n堆石子中每次选一堆,取任意个石子,而NIMK游戏是在n堆石子中每次选择k堆,1<=k<=n,从这k堆中每堆里都取出任意数目的石子,取的石子数可以不同,其他规则相同。

对于普通的NIM游戏,我们采取的是对每堆的SG值进行异或,异或其实就是对每一个SG值二进制位上的数求和然后模2,比如说3^5就是011+101=112,然后对每一位都模2就变成了110,所以3^5=6。而NIMK游戏和NIM游戏的区别就在于模的不是2,如果是取k堆,就模k+1,所以取1堆的普通NIM游戏是模2。当k=2时,3^5→011+101=112,对每一位都模3之后三位二进制位上对应的数仍然是1,1,2。那么当且仅当每一位二进制位上的数都是0的时候,先手必败,否则先手必胜。

下面是NIMK游戏的模板。

[cpp]  view plain  copy
  1. const int MAXN = 10005;  
  2. int SG[MAXN];//需要处理的SG值数组  
  3. int XOR[MAXN];//储存每一个二进制位上的和  
  4. int xxx;//储存每一个SG值的临时变量  
  5. int num;//储存当前SG值有多少位的临时变量  
  6. int maxn;//储存最大的SG值位数  
  7.   
  8. bool solve(int N,int M)//N表示SG数组的大小,从1到N,M表示每次可以取1到M堆  
  9. {  
  10.     memset(XOR, 0, sizeof XOR);  
  11.     maxn = -1;  
  12.     for (int i = 1; i <= N; i++)  
  13.     {  
  14.         xxx = SG[i];  
  15.         num = 0;  
  16.         while (xxx)  
  17.         {  
  18.             XOR[num] += xxx&1;  
  19.             num++;  
  20.             xxx >>= 1;  
  21.         }  
  22.         maxn = max(maxn, num);  
  23.     }  
  24.     for (int i = 0; i < maxn; i++)  
  25.         if (XOR[i] % (M + 1))  
  26.             return true;//返回true表示先手必胜  
  27.     return false;//返回false表示先手必败  
  28. }  



POJ-2315就是一道NIMK的标准题目,SG打个表找出规律,然后直接套用模板就行了


[cpp]  view plain  copy
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <string>  
  5. #include <cstdlib>  
  6. #include <cmath>  
  7. #include <vector>  
  8. #include <queue>  
  9. #include <map>  
  10. #include <algorithm>  
  11. #include <set>  
  12. #include <functional>  
  13. using namespace std;  
  14. typedef long long LL;  
  15. typedef unsigned long long ULL;  
  16. const int INF = 1e9 + 5;  
  17. const int MAXN = 50;  
  18. const int MOD = 1000000007;  
  19. const double eps = 1e-8;  
  20. const double PI = acos(-1.0);  
  21.   
  22. int SG[MAXN];//需要处理的SG值数组  
  23. int XOR[MAXN];//储存每一个二进制位上的和  
  24. int xxx;//储存每一个SG值的临时变量  
  25. int num;//储存当前SG值有多少位的临时变量  
  26. int maxn;//储存最大的SG值位数  
  27.   
  28. bool solve(int N,int M)//N表示SG数组的大小,从1到N,M表示每次可以取1到M堆  
  29. {  
  30.     memset(XOR, 0, sizeof XOR);  
  31.     maxn = -1;  
  32.     for (int i = 1; i <= N; i++)  
  33.     {  
  34.         xxx = SG[i];  
  35.         num = 0;  
  36.         while (xxx)  
  37.         {  
  38.             XOR[num] += xxx&1;  
  39.             num++;  
  40.             xxx >>= 1;  
  41.         }  
  42.         maxn = max(maxn, num);  
  43.     }  
  44.     for (int i = 0; i < maxn; i++)  
  45.         if (XOR[i] % (M + 1))  
  46.             return true;//返回true表示先手必胜  
  47.     return false;//返回false表示先手必败  
  48. }  
  49.   
  50. int N, M, L, R;  
  51. int s;  
  52.   
  53. int main()  
  54. {  
  55.     while (scanf("%d%d%d%d", &N, &M, &L, &R) != EOF)  
  56.     {  
  57.         s = L / (2 * PI*R);  
  58.         for (int i = 1; i <= N; i++)  
  59.         {  
  60.             scanf("%d", &SG[i]);  
  61.             SG[i] = SG[i] / (2 * PI*R) + 1;  
  62.             SG[i] %= s + 1;//打表找出的SG值的规律  
  63.         }  
  64.         if (solve(N, M))  
  65.             printf("Alice\n");  
  66.         else  
  67.             printf("Bob\n");  
  68.     }  
  69. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值