1. 程式人生 > 實用技巧 >尼姆博弈(Nim GameNim Game)

尼姆博弈(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");
	}
}