POJ-1067 取石子游戲 解題報告

Description

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

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


       題目鏈接:http://poj.org/problem?id=1067

       解法類型:威佐夫博奕

       解題思路:就是一個經典的威作福博弈,知道公式就很水了。現給出我看到的一個很牛叉的證明:

  • 簡單分析一下,容易知道兩堆石頭地位是一樣的,我們用餘下的石子數(a,b)來表示狀態,並畫在平面直角座標系上。

    用之前的定理: 有限個結點的無迴路有向圖有唯一的核  中所述的方法尋找必敗態。先標出(0,0),然後劃去所有(0,k),(k,0),(k,k)的格點;然後找y=x上方未被劃去的格點,標出(1,2),然後劃去(1,k),(k,2),(1+k,2+k),同時標出對稱點(2,1),劃去(2,k),(1,k),(2+k,1+k);然後在未被劃去的點中在y=x上方再找出(3,5)。。。按照這樣的方法做下去,如果只列出a<=b的必敗態的話,前面的一些是(0,0),(1,2),(3,5),(4,7),(6,10),…

    接下來就是找規律的過程了,忽略(0,0),記第n組必敗態爲(a[n],b[n])

    命題一:a[n+1]=前n組必敗態中未出現過的最小正整數

    [分析]:如果a[n+1]不是未出現的數中最小的,那麼可以從a[n+1]的狀態走到一個使a[n+1]更小的狀態,和我們的尋找方法矛盾。

    命題二:b[n]=a[n]+n

    [分析]:歸納法:若前k個必敗態分別爲 ,下證:第k+1個必敗態爲

    從該第k+1個必敗態出發,一共可能走向三類狀態,從左邊堆拿走一些,從右邊堆拿走一些,或者從兩堆中拿走一些.下面證明這三類都是勝態.

    情況一:由命題一,任意一個比a[k+1]小的數都在之前的必敗態中出現過,一旦把左邊堆拿少了,我們只要再拿成那個數相應的必敗態即可。

    情況二(從右邊堆拿走不太多):這使得兩堆之間的差變小了,比如拿成了 ,則可再拿成

    情況二(從右邊堆拿走很多):使得右邊一堆比左邊一堆更少,這時類似於情況一,比如拿成了 (其中a[m] ;

    情況三:比如拿成 ,則可再拿成

    綜上所述,任何從 出發走向的狀態都可以走回核中.故原命題成立.

    以上兩個命題對於確定(a[n],b[n])是完備的了,給定(0,0)然後按照這兩個命題,就可以寫出(1,2),(3,5),(4,7),…

    這樣我們得到了這個數列的遞推式,以下我們把這兩個命題當成是(a[n],b[n])的定義。

    先證明兩個性質:

    性質一:核中的a[n],b[n]遍歷所有正整數。

    [分析]:由命題一,二可得a[n],b[n]是遞增的,且由a[n]的定義顯然。

    性質二:A={a[n]:n=1,2,3,…},B={b[n]:n=1,2,3,…},則集合A,B不交。

    [分析]:由核是內固集,顯然。

    看到這裏大家有沒有想到Beatty序列呢,實際上a[n]和b[n]就是一個Beatty序列。

    ,有 ,解方程

    ,到此,我們找到了該必敗態的通項公式。

    實際上這組Beatty序列還有一些別的性質,比如當一個數是Fibonacci數的時候,另一個數也是Fibonacci數;而且兩者的比值也越來越接近黃金比,這些性質在得到通項公式之後不難證明。

    總的來說,這個問題給我們了哪些啓示呢?首先用定理所說的方法找核,然後給出核的規律(遞推,或是通項)並且證明。最後附上一張對應的必敗態圖.

    wythoff

    PS:用圖來表示博弈過程,好想法啊。~


    算法實現:

    //STATUS:C++_AC_32MS_172k
    #include<stdio.h>
    #include<math.h>
    const double x=((sqrt(5.0)+1)/2);
    int main()
    {
    //	freopen("in.txt","r",stdin);
    	int a,b,k,ta;
    	while(~scanf("%d%d",&a,&b))
    	{
    		if(a>b){int t=a;a=b,b=t;}
    		k=b-a;
    		ta=(int)floor(k*x);
    		printf("%d\n",ta==a?0:1);
    	}
    	return 0;
    }

發佈了74 篇原創文章 · 獲贊 232 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章