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; }
3、Nim游戲
有n堆石子,每堆石子的數量為 x1, x2, x3,x4......xn。給定k個數a1,a2,a3,……ak 兩人輪流取,每人每次只能選取一堆,
從中取出一些,每次所取的數量一定在a1,a2,a3,……ak中,拿走最后一根的人獲勝。
我們可以將游戲分解,把每一堆看成是一個子游戲。
比如,有3堆石子,每堆石子的數目,為5,6,7,可以取的石子的數目是 {1,3,4}
可以找出每堆石子的所有后繼狀態,看成是n枚棋子在有向圖上移動,甲乙雙方人選一個子游戲並移動上面的棋子。
看起來狀態非常多,也很復雜,所以我們需要借鑒SG函數
首先定義一個mex 運算,表示最小的不屬於這個集合的非負整數。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
對於一個給定的有向無環圖,關於每個頂點的SG函數定義如下:
g(x)=mex{g(y)|y是x的所有后繼狀態};沒有出邊的頂點,SG值為0,因為它的后繼集合為空。
對於n枚棋子,它們對應頂點的SG值分別為:(a1,a2,……,an)再設局面 (a1,a2,……,an)時Nim游戲的一種必勝策略是 將ai變成k,那么游戲的一種必勝策略就是
把第i枚棋子移動到SG值為k的頂點上;

#include<iostream> using namespace std; /*輸入堆數 n,每堆的石子數a[] 輸入限定選擇的數目 k, 是s[] */ int max(int a[],int n){ int m=0; for(int i=0;i<n;i++){ if(m>a[i]) m=a[i]; } return m; } int main(){ int n,a[100],i,j,s[100],k,*vis,*grund; while(cin>>n){ for(i=0;i<n;i++) cin>>a[i]; int size=max(a,n); vis=new int[size+1]; grund=new int[size+1]; grund[0]=0; cin>>k; for(i=0;i<k;i++) cin>>s[i]; // for(i=1;i<=size;i++){ //i是石子的數目,a[j]是每次應當取的石子數 i-a[j] 剩余的石子數 memset(vis,0,sizeof(vis)); for(j=0;j<k;j++){ if(s[j]<=i){ vis[grund[i-s[j]]]=1; } for(int k=1;;k++){ if(vis[k]==0) grund[i]=k; break; } } } int x=0; for(i=0;i<n;i++) x^=grund[a[i]]; if(x==0) cout<<"先手勝"<<endl; else cout<<"后手勝"<<endl; } return 0; }
很好的博客對博弈的解釋:http://www.cppblog.com/MiYu/archive/2012/08/14/124649.html#187193