1. 程式人生 > >【博弈】威佐夫博弈poj1067 取石子游戲

【博弈】威佐夫博弈poj1067 取石子游戲

最後居然能跟黃金分割搭上關係,這也太神奇了.....

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

這種規則下游戲是頗為複雜的。我們用(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,需要分多種情況考慮,假設面對的局勢是(a,b),(我們規定a<=b)

1.若a=b,則同時從兩堆取走a個,局勢變成(0,0)的奇異局勢.

2.若a是某個奇異局勢的a[k],且b>b[k],則從b中取出b-b[k]個,局勢變成(a[k],b[k])的奇異局勢.

3.若a是某個奇異局勢的a[k],且b<b[k],兩堆之差為b-a個,同時從兩堆中取出a[k]-a[b-a]個,局勢變成(a[b-a],b[b-a])的奇異局勢.

4.若b是某個奇異局勢的b[k],且a>a[k],則從a中取出a-a[k]個,局勢變成(a[k],b[k])的奇異局勢.

5.若b是某個奇異局勢的b[k],且a<a[k],則一定有a=b[j](j<k).此時從b中取出b-a[j]個,局勢變成(a[j],b[j])的奇異局勢.

由此,性質3得證。

可以看出,如果兩人都採取正確的操作,那麼對於非奇異局勢,先拿者必勝,對於奇異局勢,先拿者必敗。

對於奇異局勢,有如下公式:

a[k]=[k*(1+√5)/2],b[k]=a[k]+k。(k=0,1,2......,[]表示取整)

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

  1. #include <stdio.h>
  2. #include <math.h>
  3. constdouble Gsr=(1+sqrt(5.0))/2;  
  4. void swap(int &a,int &b)  
  5. {  
  6.     int t=b;  
  7.     b=a;  
  8.     a=t;  
  9. }  
  10. int main()  
  11. {  
  12.     int a,b;  
  13.     while(~scanf("%d%d",&a,&b))  
  14.     {  
  15.         if(a>b)  
  16.             swap(a,b);  
  17.         if(a == (int)(Gsr*(b-a)))   //如果是奇異局勢,先拿者輸
  18.             puts("First Lose");  
  19.         else
  20.             puts("First Win");  
  21.     }  
  22.     return 0;  
  23. }