博弈问题之三种基础博弈小结

来源:互联网 发布:蒋媛是现代版知画 编辑:程序博客网 时间:2024/05/14 10:32
 

博弈问题之三种基础博弈小结

分类: 博弈 177人阅读 评论(0) 收藏 举报
个人总结

博弈论:

·博弈论(Game Theory),亦名“对策论”、“赛局理论”,属应用数学的一个分支, 博弈论已经成为经济学的标准分析工具之一。目前在生物学、经济学、国际关系、计算机科学、政治学、军事战略和其他很多学科都有广泛的应用。博弈论主要研究公式化了的激励结构间的相互作用。是研究具有斗争或竞争性质现象的数学理论和方法。也是运筹学的一个重要学科。 博弈论考虑游戏中的个体的预测行为和实际行为,并研究它们的优化策略。生物学家使用博弈理论来理解和预测进化论的某些结果。

一、基础博弈:

(一)巴什博弈:

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。

例题:

HDU2149 Public Sale

题意中文我就不多说了,自己看。这是一个典型的巴什博弈,只要m%(n+1)==0就是后手赢。难点在于先手赢的情况是要输出所有可能的第一次出价情况。需要判断n是否大于等于m,如果n>=m则m~n的所有数都是符合情况的,而m>n时,第一次取的是m除以(n+1)的余数,因为只有让后手者保持(n+1)的倍数才能先手必胜。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int n,m,i;  
  40.     while(scanf("%d%d",&m,&n)!=EOF)  
  41.     {  
  42.         if(m%(n+1)==0)  
  43.         {  
  44.             printf("none\n");  
  45.         }  
  46.         else  
  47.         {  
  48.             if(m<=n)  
  49.             {  
  50.                 printf("%d",m);  
  51.                 FOR(m+1,n,i)  
  52.                 {  
  53.                     printf(" %d",i);  
  54.                 }  
  55.                 printf("\n");  
  56.             }  
  57.             else  
  58.             {  
  59.                 printf("%d\n",m%(n+1));  
  60.             }  
  61.         }  
  62.     }  
  63.     return 0;  
  64. }  

HDU2188 悼念512汶川大地震遇难同胞——选拔志愿者

这题给人一种中国正能量,必做的巴什博弈题。思路一样,n%(m+1)==0后手必胜,否则先手必胜。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int n,m,t;  
  40.     RD(t);  
  41.     while(t--)  
  42.     {  
  43.         RD(n);  
  44.         RD(m);  
  45.         if(n%(m+1)==0)  
  46.         {  
  47.             printf("Rabbit\n");  
  48.         }  
  49.         else  
  50.         {  
  51.             printf("Grass\n");  
  52.         }  
  53.     }  
  54.     return 0;  
  55. }  


POJ2368 Buttons

巴什博弈的变形,可能不太看出来的这是一个巴什博弈,题意是给你一个k,让你求出最小的l使得k%(l+1)==0。。。这下就有点明显了,之前都是问你先手还是后手,这次问你的是取最大的n的最小情况。思路:求出k的所有因数,从小到大排序后,输出第一个大于2的数减1,如果没有输出0。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int k,i,j,a[10001],f;  
  40.     while(scanf("%d",&k)!=EOF)  
  41.     {  
  42.         mem(a,0);  
  43.         j=0;  
  44.         for(i=1; i*i<=k; ++i)  
  45.         {  
  46.             if(k%i==0)  
  47.             {  
  48.                 a[j++]=i;  
  49.                 a[j++]=k/i;  
  50.             }  
  51.         }  
  52.         sort(a,a+j);  
  53.         f=0;  
  54.         For(0,j,i)  
  55.         {  
  56.             if(a[i]>2)  
  57.             {  
  58.                 f=1;  
  59.                 printf("%d\n",a[i]-1);  
  60.                 break;  
  61.             }  
  62.         }  
  63.         if(f==0)  
  64.         {  
  65.             printf("0\n");  
  66.         }  
  67.     }  
  68.     return 0;  
  69. }  


(二)威佐夫博奕:

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk= ak + k,奇异局势有如下三条性质:
1、任何自然数都包含在一个且仅有一个奇异局势中。由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性质1成立。
2、任意操作都可将奇异局势变为非奇异局势。事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3、采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);如果a = ak ,b > bk,那么,取走b  – bk个物体,即变为奇异局势;如果 a = ak ,  b < bk ,则同时从两堆中拿走 ak – ab – ak个物体,变为奇异局势( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k),从第二堆里面拿走 b – bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b – aj 即可。
从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:ak =[k(1+√5)/2],bk= ak + k  (k=0,1,2,…,n 方括号表示取整函数)
奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618…,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。


例题:

POJ1067 取石子游戏

典型的威佐夫博弈,和威佐夫博弈提到的例子一样,直接定义做就行。判断两堆中较小堆数量是否与两堆数量差值乘以(1+√5)/2相等,若相等就是先手必输,反之先手必胜。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int a,b,x;  
  40.     double c=(sqrt(5.0)+1.0)/2.0;  
  41.     while(scanf("%d%d",&a,&b)!=EOF)  
  42.     {  
  43.         x=abs(a-b);  
  44.         x=x*c;  
  45.         if(x==min(a,b))  
  46.         {  
  47.             printf("0\n");  
  48.         }  
  49.         else  
  50.         {  
  51.             printf("1\n");  
  52.         }  
  53.     }  
  54.     return 0;  
  55. }  

HDU2177 取(2堆)石子游戏

题目要求判断是先手还是后手胜利,如果先手胜利,需要输出胜利情况先第一次取石子后两堆石子的情况。这就是一个找下一个奇态局势的题目。首先要判断的是两堆同时减去一个数,因为两堆差值不变,还是奇态局势,然后再for循环查找a和b之间,a以内的减去得到奇态局势的数。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int a,b,x,y,i;  
  40.     double c=(sqrt(5.0)+1.0)/2.0;  
  41.     while(1)  
  42.     {  
  43.         RD(a);  
  44.         RD(b);  
  45.         if(a==0&&b==0)  
  46.         {  
  47.             break;  
  48.         }  
  49.         x=b-a;  
  50.         x=x*c;  
  51.         if(x==a)  
  52.         {  
  53.             printf("0\n");  
  54.         }  
  55.         else  
  56.         {  
  57.             printf("1\n");  
  58.             if(x<a)  
  59.             {  
  60.                 printf("%d %d\n",x,b-a+x);  
  61.             }  
  62.             for(i=b;i>=a;--i)  
  63.             {  
  64.                 y=(i-a)*c;  
  65.                 if(y==a)  
  66.                 {  
  67.                     printf("%d %d\n",a,i);  
  68.                     break;  
  69.                 }  
  70.             }  
  71.             for(i=a;i>=0;--i)  
  72.             {  
  73.                 y=(a-i)*c;  
  74.                 if(y==i)  
  75.                 {  
  76.                     printf("%d %d\n",i,a);  
  77.                     break;  
  78.                 }  
  79.             }  
  80.         }  
  81.     }  
  82.     return 0;  
  83. }  

(三)尼姆博弈:

有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。
计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号(+)表示这种运算。这种运算和一般加法不同的一点是1+1=0。先看(1,2,3)的按位模2加的结果:
1 =二进制01
2 =二进制10
3 =二进制11 (+)
———————
0 =二进制00 (注意不进位)

对于奇异局势(0,n,n)也一样,结果也是0。
任何奇异局势(a,b,c)都有a(+)b(+)c =0。
如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a(+)b,即可,因为有如下的运算结果: a(+)b(+)(a(+)b)=(a(+)a)(+)(b(+)b)=0(+)0=0。要将c 变为a(+)b,只要从 c中减去 c-(a(+)b)即可。

例题:

POJ2234 Matches Game

一道最简单的尼姆博弈题,与内容上说的例子一样。只需要简单异或就行了,最后判断是否为零,若是零就是后手必胜,反之先手。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int t,ans,x;  
  40.     while(scanf("%d",&t)!=EOF)  
  41.     {  
  42.         ans=0;  
  43.         while(t--)  
  44.         {  
  45.             RD(x);  
  46.             ans^=x;  
  47.         }  
  48.         if(ans==0)  
  49.         {  
  50.             printf("No\n");  
  51.         }  
  52.         else  
  53.         {  
  54.             printf("Yes\n");  
  55.         }  
  56.     }  
  57.     return 0;  
  58. }  

POJ2975 Nim

题意:和尼姆博弈的内容一样,只是如果是先手胜的话就输出能赢的方法总数。思路:首先是将所有石堆的石子数量异或一下得到数值判断能否获胜,若能获胜,在再用该数值与各石堆数量分别异或,这就是得到了取走多少石子能让该堆石子变为必败态的数量,判断是否小于该石堆的数量,是就加1。最后输出总数。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int n,i,s[1001],sum,c;  
  40.     while(1)  
  41.     {  
  42.         RD(n);  
  43.         if(n==0)  
  44.         {  
  45.             break;  
  46.         }  
  47.         sum=0;  
  48.         FOR(1,n,i)  
  49.         {  
  50.             RD(s[i]);  
  51.             sum^=s[i];  
  52.         }  
  53.         c=0;  
  54.         if(sum!=0)  
  55.         {  
  56.             FOR(1,n,i)  
  57.             {  
  58.                 if((sum^s[i])<s[i])  
  59.                 {  
  60.                     c++;  
  61.                 }  
  62.             }  
  63.         }  
  64.         printf("%d\n",c);  
  65.     }  
  66.     return 0;  
  67. }  

POJ1704 Georgia and Bob

阶梯尼姆博弈,尼姆博弈的变形。题意是有n个格子,某些里面有石子,我们可以将石子左移任意格数,但不能越过前方的石子。我们可以将最左边的石子当做终点,将石子两两分为一组,前后两组的格数差距是没有意义的,因为移动前一组的后一个,后一组的前一个总能移动相应格数。所以只需要考虑同一组的前后的格数差,就等同于尼姆博弈中的石堆。所以最后只需要异或起来就行了。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int main()  
  38. {  
  39.     int t,n,a[10001],s,i;  
  40.     RD(t);  
  41.     while(t--)  
  42.     {  
  43.         RD(n);  
  44.         s=0;  
  45.         FOR(1,n,i)  
  46.         {  
  47.             RD(a[i]);  
  48.         }  
  49.         if(n%2==1)  
  50.         {  
  51.             a[++n]=0;  
  52.         }  
  53.         sort(a+1,a+n+1);  
  54.         for(i=n; i>=1; i-=2)  
  55.         {  
  56.             s^=(a[i]-a[i-1]-1);  
  57.         }  
  58.         if(s!=0)  
  59.         {  
  60.             printf("Georgia will win\n");  
  61.         }  
  62.         else  
  63.         {  
  64.             printf("Bob will win\n");  
  65.         }  
  66.     }  
  67.     return 0;  
  68. }  
 

博弈问题之SG函数博弈小结

分类: 博弈 搜索 187人阅读 评论(0) 收藏 举报
个人总结

SG函数:

给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移 动者判负。事实上,这个游戏可以认为是所有Impartial Combinatorial Games的抽象模型。也就是说,任何一个ICG都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。下 面我们就在有向无环图的顶点上定义Sprague-Garundy函数。首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

在我的理解中,sg函数就是一个对有向无环图dfs的过程,在处理nim博弈时,多个石堆可以看成多个sg函数值的异或。

例题:

POJ2311 Cutting Game

典型的sg博弈,找后继状态。题意是给出一个n*m的纸片,每次剪成两部分,谁先剪到1*1就胜利。这就是一个找后继的题目,每次剪成的两部分就是前一状态的后继,只要将两个部分的sg值异或起来就是前一状态的sg值。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int sg[201][201];  
  38. int dfs(int a,int b)//sg函数的一般写法  
  39. {  
  40.     if(sg[a][b]>=0)  
  41.     {  
  42.         return sg[a][b];  
  43.     }  
  44.     int i,map[201],r;//map标记数组一定要在dfs内部定义,不然会出现错误  
  45.     mem(map,0);  
  46.     FOR(2,(a/2),i)  
  47.     {  
  48.         r=dfs(i,b)^dfs(a-i,b);//后继的异或得到前一状态的sg值  
  49.         map[r]=1;  
  50.     }  
  51.     FOR(2,(b/2),i)  
  52.     {  
  53.         r=dfs(a,i)^dfs(a,b-i);  
  54.         map[r]=1;  
  55.     }  
  56.     FOR(0,200,i)  
  57.     {  
  58.         if(map[i]==0)  
  59.         {  
  60.             return sg[a][b]=i;//mex公式的应用  
  61.         }  
  62.     }  
  63. }  
  64. int main()  
  65. {  
  66.     int n,m,sum;  
  67.     mem(sg,-1);  
  68.     while(scanf("%d%d",&n,&m)!=EOF)  
  69.     {  
  70.         sum=dfs(n,m);  
  71.         if(sum>0)  
  72.         {  
  73.             printf("WIN\n");  
  74.         }  
  75.         else  
  76.         {  
  77.             printf("LOSE\n");  
  78.         }  
  79.     }  
  80.     return 0;  
  81. }  

POJ2425 A Chess Game

题意是给你一个拓扑图,一个起点上的n个棋子,两个玩家交替移动棋子,谁无法移动谁输,典型的sg函数运用。套用模板就行了。此题数据量较大,加入了输入优化后刷到了第一版第四名,nice!

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int vis[1001][1001],sg[1001];  
  38. int n;  
  39. int dfs(int x)//典型的sg过程  
  40. {  
  41.     int i;  
  42.     if(sg[x]!=-1)  
  43.     {  
  44.         return sg[x];  
  45.     }  
  46.     int f[1001];  
  47.     mem(f,0);  
  48.     For(0,n,i)  
  49.     {  
  50.         if(vis[x][i]!=-1)  
  51.         {  
  52.             f[dfs(i)]=1;  
  53.         }  
  54.     }  
  55.     i=0;  
  56.     while(f[i])  
  57.     {  
  58.         i++;  
  59.     }  
  60.     return sg[x]=i;  
  61. }  
  62. int main()  
  63. {  
  64.     int i,j,k,t,x,p,sum;  
  65.     while(scanf("%d",&n)!=EOF)  
  66.     {  
  67.         mem(vis,-1);  
  68.         mem(sg,-1);  
  69.         For(0,n,i)  
  70.         {  
  71.             RD(k);  
  72.             if(k==0)  
  73.             {  
  74.                 sg[i]=0;  
  75.             }  
  76.             For(0,k,j)  
  77.             {  
  78.                 RD(t);  
  79.                 vis[i][t]=1;//建图  
  80.             }  
  81.         }  
  82.         while(1)  
  83.         {  
  84.             RD(x);  
  85.             if(x==0)  
  86.             {  
  87.                 break;  
  88.             }  
  89.             sum=0;  
  90.             For(0,x,i)  
  91.             {  
  92.                 RD(p);  
  93.                 sum^=dfs(p);  
  94.             }  
  95.             if(sum!=0)  
  96.             {  
  97.                 printf("WIN\n");  
  98.             }  
  99.             else  
  100.             {  
  101.                 printf("LOSE\n");  
  102.             }  
  103.         }  
  104.     }  
  105.     return 0;  
  106. }  

POJ2068 Nim

题意是圆桌上有2n个人,奇数一队,偶数一队,每个人都有一个拿走棋子的最高限额,问你最后1对能否获胜。

还是用强大的sg函数过的,记录下每个状态的sg。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #include <queue>  
  11. #include<map>  
  12. #define mem(a,b) memset(a,b,sizeof(a))  
  13. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  14. #define For(a,b,i) for(i=a;i<b;++i)  
  15. using namespace std;  
  16. inline void RD(int &ret)  
  17. {  
  18.     char c;  
  19.     do  
  20.     {  
  21.         c=getchar();  
  22.     }  
  23.     while(c<'0'||c>'9');  
  24.     ret=c-'0';  
  25.     while((c=getchar())>='0'&&c<='9')  
  26.     {  
  27.         ret=ret*10+(c-'0');  
  28.     }  
  29. }  
  30. inline void OT(int a)  
  31. {  
  32.     if(a>=10)  
  33.     {  
  34.         OT(a/10);  
  35.     }  
  36.     putchar(a%10+'0');  
  37. }  
  38. int n,s,m[22],sg[22][8200],sum;  
  39. int dfs(int x,int y)  
  40. {  
  41.     if(sg[x][y]!=-1)  
  42.     {  
  43.         return sg[x][y];  
  44.     }  
  45.     int i,j;  
  46.     FOR(1,m[x],i)  
  47.     {  
  48.         if(y-i<0)  
  49.         {  
  50.             break;  
  51.         }  
  52.         if(x+1>=2*n)  
  53.         {  
  54.             j=0;  
  55.         }  
  56.         else  
  57.         {  
  58.             j=x+1;  
  59.         }  
  60.         if(dfs(j,y-i)==0)  
  61.         {  
  62.             return sg[x][y]=1;  
  63.         }  
  64.     }  
  65.     return sg[x][y]=0;  
  66. }  
  67. int main()  
  68. {  
  69.     int i;  
  70.     while(1)  
  71.     {  
  72.         RD(n);  
  73.         if(n==0)  
  74.         {  
  75.             break;  
  76.         }  
  77.         RD(s);  
  78.         For(0,2*n,i)  
  79.         {  
  80.             RD(m[i]);  
  81.         }  
  82.         mem(sg,-1);  
  83.         FOR(0,2*n,i)  
  84.         {  
  85.             sg[i][0]=1;  
  86.         }  
  87.         sum=dfs(0,s);  
  88.         if(sum==0)  
  89.         {  
  90.             printf("0\n");  
  91.         }  
  92.         else  
  93.         {  
  94.             printf("1\n");  
  95.         }  
  96.     }  
  97.     return 0;  
  98. }  

POJ3537 Crosses and Crosses

题意:给出一个1*n的矩形,上面有n个方格,现有两人分别在上面画×,谁先能画出三个×相连就赢了。这就是一个sg函数的母问题转化为子问题的题目,由于在第x位置画了×后,则就转变成(x-3)个格子画×和(n-x-2)个格子画×。。。这就能不断的分解下去,最后将所有sg值异或起来就是正解了。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #include <queue>  
  11. #include<map>  
  12. #define mem(a,b) memset(a,b,sizeof(a))  
  13. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  14. #define For(a,b,i) for(i=a;i<b;++i)  
  15. using namespace std;  
  16. inline void RD(int &ret)  
  17. {  
  18.     char c;  
  19.     do  
  20.     {  
  21.         c=getchar();  
  22.     }  
  23.     while(c<'0'||c>'9');  
  24.     ret=c-'0';  
  25.     while((c=getchar())>='0'&&c<='9')  
  26.     {  
  27.         ret=ret*10+(c-'0');  
  28.     }  
  29. }  
  30. inline void OT(int a)  
  31. {  
  32.     if(a>=10)  
  33.     {  
  34.         OT(a/10);  
  35.     }  
  36.     putchar(a%10+'0');  
  37. }  
  38. int sg[2001];  
  39. int dfs(int x)  
  40. {  
  41.     if(x<0)  
  42.     {  
  43.         return 0;  
  44.     }  
  45.     if(sg[x]>=0)  
  46.     {  
  47.         return sg[x];  
  48.     }  
  49.     int i,y;  
  50.     bool v[2001]={false};  
  51.     FOR(1,x,i)  
  52.     {  
  53.         y=dfs(i-3)^dfs(x-i-2);//找后继(经典)  
  54.         v[y]=true;  
  55.     }  
  56.     for(i=0;;i++)  
  57.     {  
  58.         if(v[i]==false)  
  59.         {  
  60.             return sg[x]=i;  
  61.         }  
  62.     }  
  63. }  
  64. int main()  
  65. {  
  66.     int n,sum;  
  67.     mem(sg,-1);  
  68.     while(scanf("%d",&n)!=EOF)  
  69.     {  
  70.         sum=dfs(n);  
  71.         if(sum)  
  72.         {  
  73.             printf("1\n");  
  74.         }  
  75.         else  
  76.         {  
  77.             printf("2\n");  
  78.         }  
  79.     }  
  80.     return 0;  
  81. }  

POJ2599 A funny game

记忆化搜索,这题的博弈味道不浓,更多的是搜索。题意是给一个图,两人轮流移动,走过的节点不能再走。水题,dfs+标记就行。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. using namespace std;  
  15. inline void RD(int &ret)  
  16. {  
  17.     char c;  
  18.     do  
  19.     {  
  20.         c=getchar();  
  21.     }  
  22.     while(c<'0'||c>'9');  
  23.     ret=c-'0';  
  24.     while((c=getchar())>='0'&&c<='9')  
  25.     {  
  26.         ret=ret*10+(c-'0');  
  27.     }  
  28. }  
  29. inline void OT(int a)  
  30. {  
  31.     if(a>=10)  
  32.     {  
  33.         OT(a/10);  
  34.     }  
  35.     putchar(a%10+'0');  
  36. }  
  37. int n,v[1001][1001],vis[1001];  
  38. int dfs(int x)  
  39. {  
  40.     int i;  
  41.     FOR(1,n,i)  
  42.     {  
  43.         vis[x]=1;  
  44.         if(v[i][x]&&!vis[i])  
  45.         {  
  46.             if(!dfs(i))  
  47.             {  
  48.                 vis[x]=0;  
  49.                 return i;  
  50.             }  
  51.         }  
  52.         vis[x]=0;  
  53.     }  
  54.     return 0;  
  55. }  
  56. int main()  
  57. {  
  58.     int m,i,a,b;  
  59.     while(scanf("%d%d",&n,&m)!=EOF)  
  60.     {  
  61.         mem(v,0);  
  62.         mem(vis,0);  
  63.         FOR(1,n-1,i)  
  64.         {  
  65.             RD(a);  
  66.             RD(b);  
  67.             v[a][b]=v[b][a]=1;  
  68.         }  
  69.         i=dfs(m);  
  70.         if(i!=0)  
  71.         {  
  72.             printf("First player wins flying to airport %d\n",i);  
  73.         }  
  74.         else  
  75.         {  
  76.             printf("First player loses\n");  
  77.         }  
  78.     }  
  79.     return 0;  
  80. }  

 

博弈问题之其它博弈合集

分类: 博弈 163人阅读 评论(0) 收藏 举报
个人总结

找规律博弈:

这类题目出得也很多,而且出得很巧妙,相比固定模式的博弈解决方法,这类题目更加需要开创性思维和强大的逻辑能力。虽然最后可能代码很简单,但其中的思考过程却十分精彩。而且人都有一种在未知情况下的本能就是找出事物的规律,所以这也是人本能的一种体现~

例题:

POJ1740 A New Stone Game

这是楼教主的男人八题之一,非常好的找规律博弈,既不是很简单的一眼题,想法也很巧妙,我的男人八题专题系列有写到这个题目:传送阵


动态博弈:

动态博弈(dynamic game)是指参与人的行动有先后顺序,而且行动在后者可以观察到行动在先者的选择,并据此作出相应的选择。动态博弈的困难在于,在前一刻最优的决策在下一刻可能不再为最优,因此在求解上发生很大的困难。动态博弈行动有先后顺序,不同的参与人在不同时点行动,先行动者的选择影响后行动者的选择空间,后行动者可以观察到先行动者做了什么选择,因此,为了做最优的行动选择,每个参与人都必须这样思考问题:如果我如此选择,对方将如何应对?如果我是他,我将会如何行动?给定他的应对,什么是我的最优选择?如下棋。

其实就是DP+博弈,这类题目将博弈问题的性质糅合在了DP中,其实主要还是DP,状态转移方程是关键。

例题:

POJ1678 I Love this Game!

题意是给你n个数字和a,b(a<b),第一个人拿[a,b]中的数字,然后第二个人拿比这个数字大且相差在[a,b]之间的数字,直到那不了,问最优情况下第一个人拿的点数之和与第二个人的差。这就是一个DP过程,只要后面的人每次都取最大数即可。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #define mem(a,b) memset(a,b,sizeof(a))  
  11. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  12. #define For(a,b,i) for(i=a;i<b;++i)  
  13. #define N 1000000007  
  14. #define INF 100000000  
  15. using namespace std;  
  16. inline void RD(int &ret)  
  17. {  
  18.     char c;  
  19.     do  
  20.     {  
  21.         c=getchar();  
  22.     }  
  23.     while(c<'0'||c>'9');  
  24.     ret=c-'0';  
  25.     while((c=getchar())>='0'&&c<='9')  
  26.     {  
  27.         ret=ret*10+(c-'0');  
  28.     }  
  29. }  
  30. inline void OT(int a)  
  31. {  
  32.     if(a>=10)  
  33.     {  
  34.         OT(a/10);  
  35.     }  
  36.     putchar(a%10+'0');  
  37. }  
  38. int num[10001],dp[10001];  
  39. int main()  
  40. {  
  41.     int t,n,i,j,a,b,s;  
  42.     RD(t);  
  43.     while(t--)  
  44.     {  
  45.         RD(n);  
  46.         RD(a);  
  47.         RD(b);  
  48.         FOR(1,n,i)  
  49.         {  
  50.             scanf("%d",&num[i]);  
  51.         }  
  52.         sort(num+1,num+n+1);  
  53.         for(i=n;i>=1&&num[i]>=a;--i)  
  54.         {  
  55.             s=-INF;  
  56.             FOR(i,n,j)  
  57.             {  
  58.                 if(num[i]+b<num[j])  
  59.                 {  
  60.                     break;  
  61.                 }  
  62.                 if(num[i]+a<=num[j]&&dp[j]>s)  
  63.                 {  
  64.                     s=dp[j];  
  65.                 }  
  66.             }  
  67.             s=s>-INF?s:0;  
  68.             dp[i]=num[i]-s;  
  69.         }  
  70.         s=-INF;  
  71.         for(i=1;i<=n&&num[i]<=b;++i)  
  72.         {  
  73.             if(num[i]>=a&&dp[i]>s)  
  74.             {  
  75.                 s=dp[i];  
  76.             }  
  77.         }  
  78.         s=s>-INF?s:0;  
  79.         printf("%d\n",s);  
  80.     }  
  81.     return 0;  
  82. }  

图上博弈:

一般是无向图的删边游戏,游戏规则:在一个的无向图内,有一点作为根,两人轮流删边,脱离根的部分不用管,没有边可以删的人失败。团的性质是,删去其中任意一条边,点并不会闪架,它一定还与原图其他边相连。这样,对于这样的团,两人轮流删,一次删一条边,那关键就在于这样的团有多少条边可用来被轮流删了。
    我们还是先看下奇偶吧。
    对于一个单独的偶环,一定是先手必败,其sg=0。
    对于一个单独的奇环,一定是先手必胜,其sg=1。
    那么,在一个环团中,若有偶数条边,一定是先手必败。若有奇数条边,一定是先手必胜。
    实现时,我们可以把环团缩成一个点,环团上的每条边都作为一个与这个代表结点直接连接的一个点。这些点都是图里的子结点,sg=0,那么偶数条边,先手必败,代表结点经环上新生成的子节点异或和正好是0。如果是奇数条边,先手必胜,代表结点经环上新生成的子节点异或和正好是1。

例题:

POJ3710 Christmas Game

这题意思是给出了多个无向图进行删边游戏,且存在成环的情况。问你是谁胜利。这题的正解应该是需要用到Tarjan算法进行缩点,我是图论渣不太会,直接用的是dfs记忆化搜索+强大的sg这题和之前的POJ 2599有些类似,不过这题更麻烦而已。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #include <queue>  
  11. #include<map>  
  12. #define mem(a,b) memset(a,b,sizeof(a))  
  13. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  14. #define For(a,b,i) for(i=a;i<b;++i)  
  15. using namespace std;  
  16. inline void RD(int &ret)  
  17. {  
  18.     char c;  
  19.     do  
  20.     {  
  21.         c=getchar();  
  22.     }  
  23.     while(c<'0'||c>'9');  
  24.     ret=c-'0';  
  25.     while((c=getchar())>='0'&&c<='9')  
  26.     {  
  27.         ret=ret*10+(c-'0');  
  28.     }  
  29. }  
  30. inline void OT(int a)  
  31. {  
  32.     if(a>=10)  
  33.     {  
  34.         OT(a/10);  
  35.     }  
  36.     putchar(a%10+'0');  
  37. }  
  38. int m;  
  39. int v[101][101],vis[101],num[101];  
  40. int dfs(int x,int y)  
  41. {  
  42.     int s=0,i,j;  
  43.     vis[x]=1;  
  44.     FOR(1,m,i)  
  45.     {  
  46.         if(i!=x&&i!=y&&v[x][i]==1)  
  47.         {  
  48.             if(vis[i]==1)  
  49.             {  
  50.                 num[i]++;  
  51.                 v[x][i]=v[i][x]=0;  
  52.                 return -2;  
  53.             }  
  54.             j=dfs(i,x);  
  55.             if(j<0)  
  56.             {  
  57.                 if(num[x]==1)  
  58.                 {  
  59.                     if(j%2!=0)  
  60.                     {  
  61.                         s^=1;  
  62.                     }  
  63.                     num[x]--;  
  64.                 }  
  65.                 else  
  66.                 {  
  67.                     return --j;  
  68.                 }  
  69.             }  
  70.             else  
  71.             {  
  72.                 s^=(j+1);  
  73.             }  
  74.         }  
  75.     }  
  76.     return s;  
  77. }  
  78. int main()  
  79. {  
  80.     int n,k,i,j,a,b,sum;  
  81.     while(scanf("%d",&n)!=EOF)  
  82.     {  
  83.         sum=0;  
  84.         while(n--)  
  85.         {  
  86.             RD(m);  
  87.             RD(k);  
  88.             mem(v,0);  
  89.             mem(vis,0);  
  90.             while(k--)  
  91.             {  
  92.                 RD(a);  
  93.                 RD(b);  
  94.                 v[a][b]=1;  
  95.             }  
  96.             FOR(1,m,i)  
  97.             {  
  98.                 FOR(1,m,j)  
  99.                 {  
  100.                     if(v[i][j]==1&&v[j][i]==1)//去重边  
  101.                     {  
  102.                         v[i][j]=v[j][i]=0;  
  103.                     }  
  104.                 }  
  105.             }  
  106.             FOR(1,m,i)  
  107.             {  
  108.                 FOR(1,m,j)  
  109.                 {  
  110.                     if(v[i][j]==1)//重新转化为无向图方便操作  
  111.                     {  
  112.                         v[j][i]=1;  
  113.                     }  
  114.                 }  
  115.             }  
  116.             sum^=dfs(1,0);  
  117.         }  
  118.         if(sum==0)  
  119.         {  
  120.             printf("Harry\n");  
  121.         }  
  122.         else  
  123.         {  
  124.             printf("Sally\n");  
  125.         }  
  126.     }  
  127.     return 0;  
  128. }  

NIM积:

这个概念实在是太神奇了,看了曹钦翔大神的《从“k倍动态减法游戏”出发探究一类组合游戏问题》才对这个略知了皮毛:传送阵

这个说实在是说不清,还是自己看吧。。。

例题:

POJ3533 Light Switching Game

此题是一个很麻烦的博弈题,有1000*1000*1000的立方体,每个点上有灯,每次选择 (x1,y1,z1), (x1,y1,z2), (x1,y2,z1), (x1,y2,z2), (x2,y1,z1), (x2,y1,z2), (x2,y2,z1), (x2,y2,z2)(1 ≤ x1 ≤ x2 ≤ 1000, 1 ≤y1 ≤ y2 ≤ 1000, 1 ≤z1 ≤ z2 ≤ 1000 )点进行等状态的转换,但要求(x2,y2,z2)必须是有开着的状态,实现给你一些亮着灯的坐标,问后手是否能赢。由于是三维的nim积,所以需要用到Tartan定理,反正我具体也不清楚为什么这么求,就是直接nim_power(x,nimpower(y,z))就行了。套用nim积模板。

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4. #include<cstring>  
  5. #include<string>  
  6. #include<cmath>  
  7. #include<set>  
  8. #include<vector>  
  9. #include<stack>  
  10. #include <queue>  
  11. #include<map>  
  12. #define mem(a,b) memset(a,b,sizeof(a))  
  13. #define FOR(a,b,i) for(i=a;i<=b;++i)  
  14. #define For(a,b,i) for(i=a;i<b;++i)  
  15. using namespace std;  
  16. inline void RD(int &ret)  
  17. {  
  18.     char c;  
  19.     do  
  20.     {  
  21.         c=getchar();  
  22.     }  
  23.     while(c<'0'||c>'9');  
  24.     ret=c-'0';  
  25.     while((c=getchar())>='0'&&c<='9')  
  26.     {  
  27.         ret=ret*10+(c-'0');  
  28.     }  
  29. }  
  30. inline void OT(int a)  
  31. {  
  32.     if(a>=10)  
  33.     {  
  34.         OT(a/10);  
  35.     }  
  36.     putchar(a%10+'0');  
  37. }  
  38. int d[1001][1001];  
  39. int nim_mul(int x,int y);//两个函数互相调用(nim积模板)  
  40. int nim_pow(int x,int y)  
  41. {  
  42.     if(d[x][y]!=-1)  
  43.     {  
  44.         return d[x][y];  
  45.     }  
  46.     if(x==0)  
  47.     {  
  48.         return d[x][y]=1<<y;  
  49.     }  
  50.     if(y==0)  
  51.     {  
  52.         return d[x][y]=1<<x;  
  53.     }  
  54.     int x1=x,y1=y,s=1,w=1,z;  
  55.     while(x1||y1)  
  56.     {  
  57.         z=1<<w;  
  58.         if((x1%2==1||y1%2==1)&&!(x1%2==1&&y1%2==1))  
  59.         {  
  60.             s*=z;  
  61.         }  
  62.         w*=2;  
  63.         x1/=2;  
  64.         y1/=2;  
  65.     }  
  66.     w=1;  
  67.     x1=x;  
  68.     y1=y;  
  69.     while(x1||y1)  
  70.     {  
  71.         z=1<<w;  
  72.         if(x1%2==1&&y1%2==1)  
  73.         {  
  74.             s=nim_mul(s,z*3/2);  
  75.         }  
  76.         w*=2;  
  77.         x1/=2;  
  78.         y1/=2;  
  79.     }  
  80.     return d[x][y]=s;  
  81. }  
  82. int nim_mul(int x,int y)  
  83. {  
  84.     int i,j,s=0,p=0,q;  
  85.     for(p=0,i=x;i;i/=2,p++)  
  86.     {  
  87.         if(i%2==1)  
  88.         {  
  89.             for(q=0,j=y;j;j/=2,q++)  
  90.             {  
  91.                 if(j%2==1)  
  92.                 {  
  93.                     s^=nim_pow(p,q);  
  94.                 }  
  95.             }  
  96.         }  
  97.     }  
  98.     return s;  
  99. }  
  100. int main()  
  101. {  
  102.     int n,a,b,c,s;  
  103.     mem(d,-1);  
  104.     while(scanf("%d",&n)!=EOF)  
  105.     {  
  106.         s=0;  
  107.         while(n--)  
  108.         {  
  109.             scanf("%d%d%d",&a,&b,&c);  
  110.             s^=nim_mul(nim_mul(a,b),c);  
  111.         }  
  112.         if(s==0)  
  113.         {  
  114.             printf("Yes\n");  
  115.         }  
  116.         else  
  117.         {  
  118.             printf("No\n");  
  119.         }  
  120.     }  
  121.     return 0;  
  122. }  

还有什么不平等博弈,极大极小搜索博弈表示还没搞明白,甚至还不懂,所以以后会了再发吧~

这里给出cxlove大神的博弈总结:无线膜拜~ORZ



0 0