尼姆博弈(Nimm's Game)


尼姆博弈(Nimm's Game)

題型

尼姆博弈模型,大致上是這樣的:

有3堆各若干個物品,兩個人輪流從某一堆取任意多的物品,規定每次至少取1個,多者不限,最后取光者得勝。

 
分析
1、首先自己想一下,就會發現只要最后剩兩堆物品一樣多(不為零),第三堆為零,那面對這種局勢的一方就必敗
那我們用(a,b,c)表示某種局勢,首先(0,0,0)顯然是必敗態,無論誰面對(0,0,0) ,都必然失敗;第二種必敗態是(0,n,n),自己在某一堆拿走k(k ≤ n)個物品,不論k為多少,對方只要在另一堆拿走k個物品,最后自己都將面臨(0,0,0)的局勢,必敗。仔細分析一下,(1,2,3)也是必敗態,無論自己如何拿,接下來對手都可以把局勢變為(0,n,n)的情形
那這種奇異局勢有什么特點呢?
也不知誰這么牛逼,竟然能把這種局勢和二進制聯系在一起
這里說一種運算符號, 異或'^',a^b=a'b+ab'(a'為非a)
 
我們用符號XOR表示這種運算,這種運算和一般加法不同的一點是1 XOR 1 = 0。先看(1,2,3)的按位模2加的結果:
1 = 二進制01
2 = 二進制10
3 = 二進制11  XOR
———————
0 = 二進制00 (注意不進位)
 
對於奇異局勢(0,n,n)也一樣,結果也是0
任何奇異局勢(a,b,c)都有a XOR b XOR c = 0
 
如果我們面對的是一個非必敗態(a,b,c),要如何變為必敗態呢?
假設 a < b < c,我們只要將 c 變為a XOR b,即可。因為有如下的運算結果:
a XOR b XOR (a XOR b)=(a XOR a) XOR (b XOR b) = 0 XOR 0 = 0
要將c 變為a XOR b,只要對 c進行 c-(a XOR b)這樣的運算即可
 
2、尼姆博弈模型可以推廣到:有n堆若干個物品,兩個人輪流從某一堆取任意多的物品,規定每次至少取一個,多者不限,最后取光者得勝。
這個游戲中的變量是堆數k和各堆的物品數N1,N2,……,Nk。
對應的組合問題是,確定先手獲勝還是后手獲勝以及兩個游戲人應該如何取物品才能保證自己獲勝
 
3、為了進一步理解Nim取物品游戲,我們看看特殊情況。
如果游戲開始時只有一堆物品,先手則通過取走所有的物品而獲勝。現在設有2堆物品,且物品數量分別為N1和N2。游戲者取得勝利並不在於N1和N2的值具體是多少,而是取決於它們是否相等。
也就說兩堆的策略我們有了,現在我們如何從兩堆的取子策略擴展到任意堆數中呢?
 
首先回憶一下,每個正整數都有對應的一個二進制數,例如:57(10) = 111001(2) ,即:57(10)=25+24+23+20。於是,我們可以認為每一堆物品數由2的冪數的子堆組成。這樣,含有57枚物品大堆就能看成是分別由數量為25、24、23、20的各個子堆組成
 
現在考慮各大堆大小分別為N1,N2,……Nk的一般的Nim博弈。將每一個數Ni表示為其二進制數(數的位數相等,不等時在前面補0):
N1 = as…a1a0
N2 = bs…b1b0
……
Nk = ms…m1m0
如果每一種大小的子堆的個數都是偶數,我們就稱Nim博弈是平衡的,而對應位相加是偶數的稱為平衡位,否則稱為非平衡位。因此,Nim博弈是平衡的,當且僅當:
as +bs + … + ms 是偶數,即as XOR bs XOR … XOR ms  = 0
……
a1 +b1 + … + m1 是偶數,即a1 XOR b1 XOR … XOR m1 = 0
a0 +b0 + … + m0是偶數,即a0 XOR b0 XOR … XOR m0 = 0
  
於是,我們就能得出尼姆博弈中先手獲勝策略:
Bouton 定理先手能夠在非平衡尼姆博弈中取勝,而后手能夠在平衡的尼姆博弈中取勝。即狀態(x1, x2, x3, …, xn)為P狀態當且僅當x1 xor x2 xor x3 xor … xor xn =0。這樣的操作也稱為Nim和(Nim Sum)
我們以一個兩堆物品的尼姆博弈作為試驗。設游戲開始時游戲處於非平衡狀態。這樣,先手就能通過一種取子方式使得他取子后留給后手的是一個平衡狀態下的游戲,接着無論后手如何取子,再留給先手的一定是一個非平衡狀態游戲,如此反復進行,當后手在最后一次平衡狀態下取子后,先手便能一次性取走所有的物品而獲勝。而如果游戲開始時游戲牌平衡狀態,那根據上述方式取子,最終后手能獲
 
下面應用此獲勝策略來考慮4堆的Nim博弈。其中各堆的大小分別為7,9,12,15枚硬幣。用二進制表示各數分別為:0111,1001,1100和1111
於是可得到如下一表:

 由Nim博弈的平衡條件可知,此游戲是一個非平衡狀態的Nim博弈,因此,先手在按獲勝策略一定能夠取得最終的勝利。具體做法有多種,先手可以從大小為12的堆中取走11枚硬幣,使得游戲達到平衡(如下表)

之后,無論后手如何取子,先手在取子后仍使得游戲達到平衡

同樣的道理,先手也可以選擇大小為9的堆並取走5枚硬幣而剩下4枚,或者,先手從大小為15的堆中取走13枚而留下2枚
歸根結底, Nim博弈的關鍵在於游戲開始時游戲處於何種狀態(平衡或非平衡)和先手是否能夠按照取子游戲的獲勝策略來進行游戲
當堆數大於2時,我們看出Bouton定理依舊適用,下面用數學歸納法證明
  
證明:如果每堆都為0,顯然是P狀態(必敗)。下面驗證P狀態和N狀態的后兩個遞推關系:
一、每個N狀態都可以一步到達P狀態。
證明是構造性的。檢查Nim和X的二進制表示中最左邊一個1,則隨便挑一個該位為1的物品堆Y,根據Nim和進行調整(0變1,1變0)即可。例如Nim和為100101011,而其中有一堆為101110001。為了讓Nim和變為0,只需要讓操作的物品數取操作前的物品數和Nim的異或即可
顯然操作后物品數變小,因此和合法的。設操作前其他堆的Nim和為Z,則有Y xor Z = X。操作后的Nim和為X xor Y xor Z = X xor X = 0,是一個P狀態
二、每個P狀態(必勝態)都不可以一步到達P狀態
由於只能改變一堆的物品,不管修改它的哪一位,Nim的對應位一定不為0,不可能是P狀態。
這樣就證明了Bouton定理
 
實際解決
Nim博弈中如果規定最后取光者輸,情況是怎樣的?
初看起來問題要復雜很多(因為不能主動拿了,而要“躲着”拿,以免拿到最后一個物品),但對於Nim游戲來說,幾乎是一樣的:
首先按照普通規則一樣的策略進行,直到恰好有一個物品數大於1的堆x。在這樣的情況下,只需要把堆x中的物品拿得只剩1個物品或者拿完,讓對手面臨奇數堆物品,這奇數堆物品每堆恰好1個物品。這樣的狀態顯然是必敗的。由於你每次操作后需要保證Nim和為0,因此不可能在你操作后首次出現“恰好有一個物品數大於1的堆”。新游戲得到了完美解決
 

Being a Good Boy in Spring Festival

 

Problem Description
一年在外 父母時刻牽掛
春節回家 你能做幾天好孩子嗎
寒假里嘗試做做下面的事情吧

陪媽媽逛一次菜場
悄悄給爸爸買個小禮物
主動地 強烈地 要求洗一次碗
某一天早起 給爸媽用心地做回早餐

如果願意 你還可以和爸媽說
咱們玩個小游戲吧 ACM課上學的呢~

下面是一個二人小游戲:桌子上有M堆撲克牌;每堆牌的數量分別為Ni(i=1…M);兩人輪流進行;每走一步可以任意選擇一堆並取走其中的任意張牌;桌子上的撲克全部取光,則游戲結束;最后一次取牌的人為勝者。
現在我們不想研究到底先手為勝還是為負,我只想問大家:
——“先手的人如果想贏,第一步有幾種選擇呢?”
 
Input
輸入數據包含多個測試用例,每個測試用例占2行,首先一行包含一個整數M(1<M<=100),表示撲克牌的堆數,緊接着一行包含M個整數Ni(1<=Ni<=1000000,i=1…M),分別表示M堆撲克的數量。M為0則表示輸入數據的結束。
 
Output
如果先手的人能贏,請輸出他第一步可行的方案數,否則請輸出0,每個實例的輸出占一行。
 
Sample Input
3
5 7 9
0
 
Sample Output
1
 
分析:由上分析第1點可知,只要對 c進行 c-(a XOR b)這樣的運算即可
代碼如下:
# include<stdio.h>
int main()
{
    int nPile,i,sum,cnt;
    int p[105];
    while(scanf("%d",&nPile)&&nPile)
    {
        cnt=0;
        sum=0;
        for(i=0;i<nPile;i++)
        {
            scanf("%d",&p[i]);
            sum^= p[i];
        }
        for(i=0;i<nPile;i++)
        {
            if(p[i]>(sum^p[i]))//注意'^'的優先級小於'>'
                cnt++;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM