淺析Nim遊戲和ICG博弈
首先我們看例題:P2197 nim遊戲
題目描述
甲,乙兩個人玩Nim取石子遊戲。
nim遊戲的規則是這樣的:地上有n堆石子(每堆石子數量小於10000),每人每次可從任意一堆石子裏取出任意多枚石子扔掉,可以取完,不能不取。每次只能從一堆裏取。最後沒石子可取的人就輸了。假如甲是先手,且告訴你這n堆石子的數量,他想知道是否存在先手必勝的策略。
輸入輸出格式
輸入格式:第一行一個整數T<=10,表示有T組數據
接下來每兩行是一組數據,第一行一個整數n,表示有n堆石子,n<=10000;
第二行有n個數,表示每一堆石子的數量
輸出格式:共T行,如果對於這組數據存在先手必勝策略則輸出"Yes",否則輸出"No",不包含引號,每個單詞一行。
輸入輸出樣例
輸入樣例#1:2
2
1 1
2
1 0
輸出樣例#1:
No
Yes
講解:
本題就是最最經典的nim遊戲了。nim遊戲過程中面臨的狀態叫做局面,第一個行動的為先手,第二個行動的為後手。考慮兩人無比聰明,則必敗局面僅當該局面所能到達的局面均為必敗局面時出現,而必勝局面僅當後續局面存在至少1個必勝局面時出現,顯然nim遊戲中1為必勝局面(因為拿走1就贏了)。顯然,nim遊戲是不存在平局的,只有先手必贏或先手必輸兩種情況。
定理:設各堆為a1、a2…an,則nim遊戲先手必贏僅當a1 Xor a2
證明:
首先,當石子均被取完時,則a數組都為0,存在a1 Xor a2 Xor…Xor an=0,因為每次取都會使石子數減少,當前局面若a1 Xor a2 Xor…Xor an≠0,我們只要保證能在取走一些石子後使得a1 Xor a2 Xor…Xor an=0,則必然保證自己能取走最後一個石子獲得勝利。
等價於證明:
(1)當a1 Xor a2 Xor…Xor an≠0時,存在某種取法使得剩下的石子xor和為0。
(2)當a1 Xor a2 Xor…Xor an
首先證明(1),對於任何一個局面a1 Xor a2 Xor…Xor an=x≠0,設x的二進制最高位的1在第k位,則至少存在一堆石子ai的二進制第k位是1(因為我們是Xor運算,某一位上的1不會憑空出現)且ai≥x。由Xor運算法則知:x Xor ai<ai,(因為至少會使第k為上的1變為0)。於是我們從ai這堆裏取走一些石子,使得ai堆剩下的石子數變為ai Xor x,此時再對剩下的各堆進行上述運算:a1 Xor a2 Xor…ai Xor x…Xor an=x Xor x=0,此時Xor和為0。 於是得證(1)。
再來證明(2),對於任何一個局面a1 Xor a2 Xor…Xor ai Xor…an=0,我們反證:假設取走ai堆中的一些石子使ai變為了x,使得a1 Xor a2 Xor…Xor x Xor…an≠0,則顯然是不可能的,因為開始Xor和就為0再由Xor運算的性質當ai變為x後若Xor和為0,當且僅當ai=x時成立。而nim遊戲中不能不取,所以若當前局面Xor和為0,則必然會使下一局面Xor和不為0。於是(2)得證。
結論:nim遊戲只要滿足先手的Xor和不為0,則先手必贏,否則先手必輸。
代碼:
1 // luogu-judger-enable-o2 2 #include<bits/stdc++.h> 3 #define il inline 4 #define ll long long 5 using namespace std; 6 il ll gi() 7 { 8 ll a=0;char x=getchar();bool f=0; 9 while((x<‘0‘||x>‘9‘)&&x!=‘-‘)x=getchar(); 10 if(x==‘-‘)x=getchar(),f=1; 11 while(x>=‘0‘&&x<=‘9‘)a=a*10+x-48,x=getchar(); 12 return f?-a:a; 13 } 14 ll t,n,a[100005]; 15 int main() 16 { 17 t=gi(); 18 while(t--){ 19 n=gi();ll x=0; 20 for(int i=1;i<=n;i++)a[i]=gi(),x^=a[i]; 21 if(x)puts("Yes"); 22 else puts("No"); 23 } 24 return 0; 25 }
淺析Nim遊戲和ICG博弈