1、巴什博弈
一堆石子,有n个,两个人轮流取,每次至少取1个,至多取m个,拿走最后一个石子的人获胜
假设一堆石子有 n=m+1 由于一次只能取m个,无论先手取多少个,后手总能拿走剩余的,这时一定是先手负
于是找到取胜规则:
一对石子 n=(m+1)*r+s
对于先手应该先取走s个,设后手取走k个,先手再取走 m+1-k 剩余的石子个数为 (m+1)(r-1) 以后保持这样的取法,先取者获胜
总之,就是要留给对手 m+1的倍数
可以归结为: s=0时,后手胜
s<>0时,先手胜
2、简单的石子游戏
有n堆石子,每次至少取一根,至多拿走整堆,两人轮流拿,每次限拿其中一堆,取走最后一根的获胜。
1902年获胜策略已由美国数学家C.L.Bouton分析完成,用到的是二进制和平衡状态概念。其结论是:
对于n堆石子,第i (1<=i<=n)堆石子的个数是Xi,该状态为必败状态当且仅当 X1 XOR X2 XOR……Xn=0。
看个例子:
有4堆石子,数量分别为:7 9 12 15
二进制形式为
0111
1001
1100
1111
异或结果为:1101
1101^1001=0100=4 可以从第二堆拿走5个
1101^1100=0001=1 也可以从第三堆拿走11个
1101^1111=0010=2 或者从第四堆取走13个
比如这道题:http://www.acmicpc.sdnu.edu.cn/Problem.aspx?pid=1294
给定N堆石子,两人轮流取石子,必须先取完一堆石子才能取另一堆,而且另一堆石子的个数必须比之前取的那一堆小,每次只能取1个或者质数个石子。如果没有石子可以取了,那么他就输了。问先手是否有必胜策略。
其实对于这个游戏,我们只需要判断第一堆石子即可,也就是石子数目最少的那一堆
代码:
#include<iostream> #include<algorithm> #include<math.h> using namespace std; int isprime(int n){ int s=(int)sqrt(n); if(n==1) return 1; for(int i=2;i<=s;i++){ if(n%i==0) return 0; break; } return 1; } int main(){ int n,a[100000],x,i,j,t; while(cin>>n){ for( i=0;i<n;i++) cin>>a[i]; //每堆石子的数目 sort(a,a+n); for(j=1;j<=a[0];j++){ if(isprime(j)&&j<=a[0]) a[0]=a[0]-j; x=0; for(t=0;t<n;t++) x^=a[t]; if(x==0){ cout<<"yes"<<endl; break; }else a[0]=a[0]+j; //恢复石子数目 } if(x!=0) cout<<"no"<<endl; } return 0; }