1. 程式人生 > 其它 >尼姆博弈

尼姆博弈

尼姆博弈

題目描述

母題:有若干堆石子,每堆石子的數量是有限的,二個人依次從這些石子堆中拿取任意的石子,至少一個(不能不取),最後一個拿光石子的人勝利。

輸入示例

輸入: (1,8,9)
輸出: false
解釋: 這是奇異局勢,所以先手輸了。

解題思路

  1. 假設現在只有一堆石子,你的最佳選擇是將所有石子全部拿走,那麼你就贏了。
  2. 假設現在有兩堆石子且數量不相同,那麼你的最佳選擇是取走多的那堆石子中多出來的那幾個,使得兩堆石子數量相同,這樣,不管另一個怎麼取,你都可以在另一堆中和他取相同的個數,這樣的局面你就是必勝。
  3. 假設現在有三堆石子 ,我們用(a,b,c)表示某種局勢,首 先(0,0,0)顯然是奇異局勢,無論誰面對奇異局勢,都必然失敗。第二種奇異局勢是 (0,n,n),只要與對手拿走一樣多的物品,與假設(2)
    類似。最後都將導致(0,0,0)。仔細分析一下,(1,2,3)也是奇異局勢,無論對手如何拿,接下來都可以變為(0,n,n)的情型。

注意:與威佐夫博弈區別在於該博弈只能從一堆中取。

一個狀態是必敗狀態當且僅當它的所有後繼都是必勝狀態, 稱之為利己態,用字母T表示。

一個狀態是必勝狀態當且僅當它至少有一個後繼是必敗狀態,稱之為利他態,用字母S表示。

證明

定義:狀態(x1,x2,x3)為必敗狀態當且僅當\(x1\bigoplus x2\bigoplus x3=0\),這裡的\(\bigoplus\)是二進位制的逐位異或操作,也成Nim和。

定理:對於任何一個S態,總能從一堆石頭中取出若干個使之成為T態。

反證法:

改變\(A[i]\)的值為\(A[i\prime ]\),即\(A[i\prime]\bigoplus A[i]\neq 0\)\(S\prime\)為利他態 。

\[S=A[1]\bigoplus A[2]\bigoplus \cdots \bigoplus A[i]\bigoplus \cdots \bigoplus A[n]=0 \tag{1} \] \[S\prime =A[1]\bigoplus A[2]\bigoplus \cdots \bigoplus A[i\prime ]\bigoplus \cdots \bigoplus A[n]=0 \tag{2} \] \[S\bigoplus S\prime =A[1]\bigoplus A[2]\bigoplus \cdots \bigoplus A[i]\bigoplus \cdots \bigoplus A[n] \bigoplus A[1]\bigoplus A[2]\bigoplus \cdots \bigoplus A[i\prime ]\bigoplus \cdots \bigoplus A[n]=0 \tag{3} \] \[\Rightarrow S\bigoplus S\prime =A[i]\bigoplus A[i\prime ]=0\bigoplus 0=0 \tag{4} \]

與已知條件矛盾。

注:\(A[i]\)表示每堆石頭數。

程式碼

public boolean helper(int[] A){
    int xor=0;
    for(int i=0;i<A.length;i++) xor^=A[i];
    if(xor!=0) return true;
    else return false;
}

拓展

尼姆博弈和巴什博弈的結合。

t堆石子,每堆石子都有n個,A和B輪流從取任意堆裡取一定的石子,每次只能從一堆裡至少取一個最多取m個,你先取,A取完者勝,問能否獲勝?(0<=m,n<=2^31))

public void helper(int[] A,int n,int m){
    int res=0;
    for(int i=0;i<A.length;i++){
        int t=n%(m+1);    //m+1的倍數 說明先手輸
        ans^=t;
    }
    if(res==0) System.out.println("B");   //後手必勝
    else System.out.println("A");         //先手必勝
}

引用

(1) 尼姆博奕(Nimm Game)_shuangde800的部落格-CSDN部落格