1. 程式人生 > >【USTC 1213】取石子遊戲(尼姆博弈)

【USTC 1213】取石子遊戲(尼姆博弈)

std col 遊戲 script pro 取石子 div ++ pre

Description

在組合博弈論中,Nim遊戲是一個非常經典的問題,Nim遊戲可描述如下:
有n堆石子,每堆石子數分別為a1, a2, …, an (ai≥0)。現有兩人輪流從這n堆中取石子,每次必須從某一堆中取任意多的石子,至少要取一個,必須從同一堆中取石子,並且不能超過這一堆石子的總數。如果某一方沒有石子可取,那麽他就輸了。
例如:

有3堆石子,分別有3, 2, 2個,A和B兩人輪流取。
A先從第2堆取1個,然後B從第1堆取3個,此時石子數分別為0, 1, 2
A又從第3堆取1個,然後B從第1堆取1個,此時石子數分別為0, 0, 1
A最後從第3堆取1個,此時所有石子都被取走,B無石子可取,所以B輸了。

C. L. Bouton給出了Nim遊戲的解法:
考慮把每堆的石子數a1, a2, …, an表示成二進制,那麽當前遊戲局面的Nim數為a1, a2, …, an的按位異或。比如在上面的例子中,3=11(2), 2=10(2), 2=10(2), 將這3個數按位異或得11(2)=3。所以3是當前遊戲局面的Nim數。
這裏不加證明地給出結論:假設遊戲雙方都非常聰明,當Nim數為0時,當前遊戲者必敗;當Nim數不為0時,當前遊戲者必勝。
再考慮上面的例子,A取走第2堆的1個石子後,石子數變為3, 1, 2,其Nim數為0,從而使得B必敗;此後A每次取石子後總能使得留給B的局面的Nim數為0,所以A最終取得了勝利。
既然你已經知道了如何判斷當前Nim遊戲局面是否必勝,那麽請完成一個稍稍復雜些的任務:
給定Nim遊戲的當前局面,如果必勝,請找出當前遊戲者需要取走多少石子才能讓對方必敗,如果有多種取石子的方式,請給出要取石子數最少的。再如上面的例子,初始時,A從第1堆取3個石子,或從第2或3堆取1個石子都可以保證B必敗,但因為後者所取的石子數最少,所以這種情況下答案為1。

Input

輸入包含多組數據。
每組數據第一行為n (1≤n≤106),表示石子的堆數。
第二行包含n個非負整數,表示每堆石子的數量,每堆石子不超過109個。註意,可以有空的石子堆。
輸入以n=0結束,不要處理這個數據。

Output

對每組數據輸出一行,為需要取走的最少的石子數,如果當前局面必敗則輸出-1

Sample Input
1
10
2
17 17
3
3 2 2
4
1 2 3 4
0
Sample Output
10
-1
1
4

由於這道題符合尼姆博弈的規則,所以寫起來還算是簡單的,詳細請見我的另一篇有關博弈論的文章吧,在這就不多加闡述了。
要註意異或運算的運算順序是在加減乘除之後的!!!!!
#include "stdio.h"
const int N=1000001;
int a[N];
int main()
{
    int i,t,n,min,p;
    while(scanf("%d",&n)!=EOF,n)
    {
        min=100000001;
        t=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            t=t^a[i];
        }
        if(t==0)
        printf("-1\n");
        else
        {
            for(i=0;i<n;i++)
            {
                p=a[i]-(t^a[i]);
                if(min>p&&p>0)
                min=p;
            } 
            printf("%d\n",min);
        }
    }
    return 0;
}

【USTC 1213】取石子遊戲(尼姆博弈)