第六章 數學問題 -------- 6.2【Nim遊戲】高僧鬥法

Curtis_發表於2019-03-21

先來看看Nim定理:

//  若干堆硬幣,二人輪流取,從一堆硬幣中取幾個  直到某個人不能取硬幣  那這個人就輸了
//  3  4  5
//  3  3    把硬幣變成相同的  那麼你就贏了  因為你可以跟著另一個人一樣的取法
//  尼姆定理:  無偏差的二人遊戲  =====  尼姆堆
/*
 *      11
 *     100
 *   ^ 101
 * ------------
 *     010
 *  先手:若非 0,必贏,因為可以把局面變成0這個局面
 *      若是 0,必輸,因為這個局面本身就是贏的,你隨便動一步,那另一個人保持跟你同步,所以最後必輸。
 */
public class Nim {

    public static void main(String[] args) {
        int []A = {3,3};
        boolean res = solve(A);
        System.out.println(res);
    }
    
    static boolean solve(int []A){
        int res = 0;
        for (int i = 0; i < A.length; i++) {
            res ^= A[i];
        }
        System.out.println(Integer.toBinaryString(res));
        return res !=0;
    }

}

再來看這道題目:

 

       我們可以將和尚從後往前(從左到右)兩兩配對,若為奇數則在最高位補充一個假想的和尚,在同一對和尚中,如果對手移動前一個和尚,你總能移動後一個和尚相同的步數,所以一對和尚的前一個和尚與前面一對和尚的後一個和尚之間有多少臺階是沒有影響的。所以只要考慮同一對和尚之間有多少臺階就行了,這樣就轉化為了Nim遊戲。

  如圖: a與b配對, c與d配對 ,那麼b與c之間的臺階是沒有影響的,無論b怎麼移動,a總能夠移動相同的步數。如果該Nim遊戲為必勝,那麼我們只要移動配對和尚中的後一和尚就好了,如果對手想要破壞這種Nim遊戲,即他想移動配對中的前一和尚,那麼我們可以移動後面一對的前一個和尚使得依然保持Nim遊戲的局勢。如果Nim遊戲必敗,那麼先手者可能想破壞Nim遊戲,那麼後手者按照剛才的方式保持Nim遊戲即可。

程式碼:

import java.util.Scanner;

//   高僧鬥法  所有這類的博弈問題都可以歸結為尼姆遊戲 Nim
/*
 *   高僧鬥法   =====>  尼姆遊戲
 *   把小和尚位置間的空隙  === >  尼姆堆
 *   偶數:兩兩組合
 *   奇數:在最高階補充一個假想的小和尚
 * 
 */
public class Test {
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String A = in.nextLine();
        String[] arrayA = A.split(" ");
        int[] NimHeap = new int[arrayA.length - 1];
        for(int i = 1;i < arrayA.length;i++)
            // 兩個和尚的間距作為尼姆堆
            NimHeap[i - 1] = Integer.valueOf(arrayA[i]) - Integer.valueOf(arrayA[i - 1]) - 1;
        int sum = 0;
        
        // i+2 就是為了用一個for實現n為奇和偶的兩種情況,奇數時最後一個數不分到組中,偶數時需要將其分到組中。
        for(int i = 0;i < NimHeap.length;i = i + 2)
            sum ^= NimHeap[i];

        if(sum == 0)
            System.out.println("-1");
        else {
            for(int i = 0;i < arrayA.length - 1;i++) {
                for(int j = 1;j + Integer.valueOf(arrayA[i]) < Integer.valueOf(arrayA[i + 1]);j++) {
                    NimHeap[i] -= j;
                    if(i != 0)
                        NimHeap[i - 1] += j;
                    sum = 0;
                    for(int k = 0;k < NimHeap.length;k = k + 2)
                        sum ^= NimHeap[k];
                    if(sum == 0) {
                        System.out.println(arrayA[i]+" "+(Integer.valueOf(arrayA[i])+j));
                        return;
                    }
                    NimHeap[i] += j;
                    if(i != 0)
                        NimHeap[i - 1] -= j;
                }
            }
        }
    }
}

結果:

 

相關文章