Nim游戏是博弈论中最经典的模型(之一),它又有着十分简单的规则和无比优美的结论 Nim游戏是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatorial Games”
Nim游戏具体游戏规则为:有n堆石子,两名选手,每个选手可以拿一堆石子中的任意多个,谁面对无石子可拿的情况时就为负。
定义:先手必败状态 定义为走不到任何一个必败状态,为状态P
先手必胜状态 定义为能够走到一个必败状态,为状态N
根据定义出发,模拟(2,2)的必败场景
因为(0,2)能到(0,0),所以(0,2)为N状态
因为(1,1)只有(0,1)一个后继态,而(0,1)为N态,所以(1,1)为P态。所以(1,2)为N态
所以(2,2)有且只有两个N态,所以(2,2)是一个P态
即面对(2,2)的情况,先手必败。
既然如此,给定n堆石子就可以求出当前状态是N或P,但是时间复杂度过高哪怕用上记忆化搜索或者DP,时间复杂度为O ( max(a1,a2...an) ),而0<=ai<=1e9
显然是不能再1s钟内算出答案。
所以数学家们想到了一个绝妙的方法:利用异或
记 t = a1^a2^a3^...^an
若t=0,则当前状态为P,即先手必败
否则则为N,即先手必胜
证明:若需证明上述定理,则需要证明
(1)最终的全零态被判定为P态
(2)被判定为N态的一定可以移动到某一个P态
(3)被判定为P态的,一定不能移动到某一个P态
所以可以直接通过上边的定理得出当前是一个P态或者N态
1 #include<iostream> 2 using namespace std; 3 int main(void){ 4 int n; 5 cin>>n; 6 int res; 7 cin>>res; 8 for(int i=1;i<n;i++){ 9 int t; 10 cin>>t; 11 res^=t; 12 } 13 if(res==0){ 14 cout<<"No"; 15 }else{ 16 cout<<"Yes"; 17 } 18 }
Nim游戏变种--台阶Nim游戏
题目:https://www.acwing.com/problem/content/894/
1 #include<iostream> 2 using namespace std; 3 int main(void){ 4 int n; 5 cin>>n; 6 int res=0; 7 for(int i=0;i<n;i++){ 8 int x; 9 cin>>x; 10 if(i%2==0) 11 res^=x; 12 } 13 if(res) puts("Yes"); 14 else puts("No"); 15 return 0; 16 }