尼姆博弈(Nim GameNim Game)
五.尼姆博弈(Nim GameNim Game)
基本問題
有三堆石子,兩人輪流取,每次可以從一堆中取走任意數量個石子,至少取走一個,問先後手誰勝。一般推廣:有nn堆石子x1,x2,x3,...,xnx1,x2,x3,...,xn,兩人輪流取,每次可以從任意一堆石子中取走至少一個石子,問先後手誰勝。
解決方法
方法很簡單,直接求所有xixi的異或和,如果異或和為00則先手必敗,否則先手必勝。形式化的表達即當且僅當x1⊕x2⊕x3⊕...⊕xn=0時,先手必敗。同理,也就是所以異或和為0的狀態是P狀態,所有異或和非0的是N狀態。
證明
首先當沒有石子的時候,先手必敗,此時所有石子的異或和為0,這個是P狀態。(必敗點)
接下來我們證明任意一個N狀態至少能夠達到一個P狀態。
假設當前所有石子個數的異或和為k,即\(⊕_{i=1}^n\)xi=k,那麼,必定存在一個xi滿足xi二進位制位上存在kk的最高位,並且不難證明xi⊕k<xi,那麼,將xi異或上k之後,剩下所有的數的異或和恰好為0,又回到了一個P狀態。
而一個P狀態的異或和為0,任何一個數減小之後異或和一定不為0,所以可以證明任何一個P狀態的後繼狀態都是N狀態。
綜上,異或和為0的狀態先手必敗,其他情況先手必勝。
拓展模式
-
每次取的石子數存在上界m
這個是Bash Game+Nim Game,只需要把所有石子按照m+1取模再考慮Nim遊戲就好了。
-
每次允許從k堆石子中取(\(Nim_k\)
我們一般考慮的情況是\(Nim_1\),我們的解法是考慮2進位制下的異或和是否為0,而異或和是不進位的加法,同理,對於\(Nim_k\)的情況,我們考慮k+1進位制下每一位不進位加法的結果,如果每一位都是0的話就是P局面,否則是N局面。
-
階梯博弈:博弈在階梯上進行,每次可以將一堆的若干式子移動到上一階去,不可操作者輸。
忽略所有的偶數階梯,只留下奇數階梯,轉化為普通的Nim遊戲。大致的思路是這樣的:首先終止狀態一定是所有石子都在0號階梯,即一個偶數階梯。那麼如果對方移動了一個偶數階梯上的石子,那麼你可以在移動結束的那個奇數階梯,直接把等數量的石子繼續向前移動,這樣子可以保證偶數階梯上的石子對於結果沒有任何影響。那麼如果移動的是一個奇數階梯,因為偶數階梯是沒有影響的,所以你可以認為移動奇數階梯就是直接被移走了,那麼這就是一個普通的Nim遊戲了。
例題
(階梯博弈)--洛谷p2575
AKN玩遊戲玩累了,於是他開始和同伴下棋了,玩的是跳棋!對手是wwx!這兩位上古神遇在一起下棋,使得棋局變得玄幻莫測,高手過招,必有一贏,他們都將用最佳策略下棋,現在給你一個n*20的棋盤,以及棋盤上有若干個棋子,問誰贏?akn先手!
遊戲規則是這樣的:
對於一個棋子,能將它向右移動一格,如果右邊有棋子,則向右跳到第一個空格,如果右邊沒有空格,則不能移動這個棋子,如果所有棋子都不能移動,那麼將輸掉這場比賽。
輸入格式
第一行一個T,表示T組資料
每組資料第一行n,表示n*20的棋盤
接下來n行每行第一個數m表示第i行有m個棋子
隨後跟著m個數pj表示第i行的棋子佈局
輸出格式
如果AKN能贏,則輸出”YES”,否則輸出”NO”
一道優秀的階梯Nim+SG定理。
首先,我們在整個序列前面加上一個空格(設此時空格個數為C+1),然後從右到左將所有空格編號 為
0至C。令第i級階梯上的棋子數為編號為ii的空格右邊的連續棋子個數。
以下用■表示棋子,□表示空格。則對於這個場景:
(□)□■□□□□□□□□□□□□□□□□■■(樣例第一組資料)
有第0至17級階梯(容易數出有18個空格)棋子個數分別是{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}。
將一個棋子移至右邊第一個空格時,相當於將其與其右邊相鄰的所有棋子移到下一級階梯。如這樣:
(□)□■□□□□□□□□□□□□□□□□■■變成
(□)□□■□□□□□□□□□□□□□□□■■時相當於
{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}變成
{2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0}(第16層一顆棋子下移一級階梯),又如:
(□)■■■□□■變成
(□)■□■■□■時相當於
{1,0,3}變成
{1,2,1}(第2層兩顆棋子下移一級階梯)。
我們發現,當所有棋子都在第0級階梯時,先手無法操作,必敗。
這就是典型的階梯博弈
於是我們處理一下,用階梯Nim的解法(SG函式為奇數位異或和)再加一個SG定理處理多行即可。
#include<cstdio>
#include<cstring>
int T,N,K,cnt,tot,x,ans1,ans2;
bool hv[23];//hv[i]==true表示i位置有石子
int main()
{
scanf(" %d",&T);
while(T--)
{
scanf(" %d",&N);ans2=0;//整個資料的SG值用ans2儲存
while(N--)
{
scanf(" %d",&K);
memset(hv,false,sizeof(hv));cnt=20-K+1;tot=0;ans1=0;//cnt即C,tot儲存當前階梯棋子個數,ans1儲存本行SG值
while(K--)
{
scanf(" %d",&x);
hv[x]=true;//標記有石子
}
for(int i=1;i<=20;++i)
{
if(!hv[i])
{
if((--cnt)&1)ans1^=tot;//奇數級階梯,異或
tot=0;
}
else ++tot;//加棋子到階梯上
}
ans2^=ans1;//SG定理應用
}
if(ans2)printf("YES\n");
else printf("NO\n");
}
return 0;
}
nim遊戲模版
Nim 遊戲的規則是這樣的:地上有 n 堆石子(每堆石子數量小於 10^4),每人每次可從任意一堆石子裡取出任意多枚石子扔掉,可以取完,不能不取。每次只能從一堆裡取。最後沒石子可取的人就輸了。假如甲是先手,且告訴你這 n 堆石子的數量,他想知道是否存在先手必勝的策略。
程式碼
#include<bits/stdc++.h>
using namespace std;
int t,n;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int ans=0;
for(int i=1; i<=n; ++i){
int shu;
scanf("%d",&shu);
ans^=shu;
}
if(!ans) printf("No\n");
else printf("Yes\n");
}
}