1. 程式人生 > >取石子游戲(威佐夫博弈)

取石子游戲(威佐夫博弈)

有兩堆石子,數量任意,可以不同。遊戲開始由兩個人輪流取石子。遊戲規定,每次有兩種不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在兩堆中同時取走相同數量的石子。最後把石子全部取完者為勝者。現在給出初始的兩堆石子的數目,如果輪到你先取,假設雙方都採取最好的策略,問最後你是勝者還是敗者。

Input

輸入包含若干行,表示若干種石子的初始情況,其中每一行包含兩個非負整數a和b,表示兩堆石子的數目,a和b都不大於1,000,000,000。

Output

輸出對應也有若干行,每行包含一個數字1或0,如果最後你是勝者,則為1,反之,則為0。

Sample Input

2 1
8 4
4 7

Sample Output

0
1
0

威佐夫博弈: (引用大佬的部落格)

有兩堆各若干個物品,兩個人輪流從某一堆或同時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最後取光者得勝。

這種規則下游戲是頗為複雜的。我們用(a[k],b[k])(a[k] ≤ b[k] ,k=0,1,2,...,n)表示兩堆物品的數量並稱其為局勢。
如果甲面對(0,0),那麼甲已經輸了,這種局勢我們稱為奇異局勢。

首先列舉人們已經發現的前幾個奇異局勢:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
通過觀察發現:a[0]=b[0]=0,a[k]是未在前面出現過的最小自然數,而 b[k]= a[k] + k。



奇異局勢有如下三條性質:
1、任何自然數都包含且僅包含在一個奇異局勢中。
2、任意操作都可以使奇異局勢變為非奇異局勢。
3、必有一種操作可以使非奇異局勢變為奇異局勢。

性質1中的存在性很好理解,對於唯一性,因為a[k]是未在前面出現過的最小自然數。所以a[k]>a[k-1],b[k] = a[k]+k > a[k-1]+k-1 + 1 > b[k-1] > a[k-1].
即b[k] > a[k] > b[k-1] > a[k-1]。所以某個自然數不會出現多於一次的情況。

性質2,我們可以嘗試遊戲規則中的兩種操作:
如果從某一堆中取,那麼a[k],b[k]中必有一個量發生變化,由性質1,則變化後的局勢不可能是奇異局勢。
如果同時從兩堆中取同樣多的物品,但由於其差(b[k]-a[k])不會改變,所以它變化後的局勢也不可能是奇異局勢。

性質3,需要分多種情況考慮,假設面對的局勢是(i,j),(我們規定i<=j)
1.若i = j,則同時從兩堆取走i個,局勢變成(0,0)的奇異局勢.
2.若i是某個奇異局勢的a[k],且j>b[k],則從j中取出 j-b[k] 個,局勢變成(a[k],b[k])的奇異局勢.
3.若i是某個奇異局勢的a[k],且j<b[k],兩堆之差為 j - i 個,同時從兩堆中取出 a[ k ] - a[ j-i ] 個,局勢變成( a[ j-i ] , b[ j-i ] )的奇異局勢.
4.若j是某個奇異局勢的b[k],且i>a[k],則從i中取出 i-a[k] 個,局勢變成(a[k],b[k])的奇異局勢.
5.若j是某個奇異局勢的b[k],且i<a[k],則一定有 i = b[m](m<k).此時從j中取出 j-a[m] 個,局勢變成(a[m],b[m])的奇異局勢.
由此,性質3得證。

可以看出,如果兩人都採取正確的操作,那麼對於非奇異局勢,先拿者必勝,對於奇異局勢,先拿者必敗。
對於奇異局勢,有如下公式:
a[k]=[k*(1+√5)/2],b[k]=a[k]+k。(k=0,1,2......,[]表示取整)

有趣的是,式中的(1+√5)/2正是黃金分割比例。

程式碼如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
    int a,b;
    while(~scanf("%d%d",&a,&b))
    {
        if(a<b)swap(a,b);
        int k=a-b;
        a=(int)(k*(1+sqrt(5.0))/2.0);
        if(a==b)
            printf("0\n");
        else
            printf("1\n");
    }
}