1. 程式人生 > 實用技巧 >威佐夫博奕

威佐夫博奕

威佐夫博奕

威佐夫博奕(Wythoff Game):有兩堆各若干個物品,兩個人輪流從某一堆或同
時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最後取光者得勝。

這種情況下是頗為複雜的。我們\((a_k,b_k)\)\(a_k\le 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)。
我們會發現他們的差值是遞增的,分別是0,1,2,3,4,5,6,7......n

可以看出\(a_0=b_0=0\)


對於奇異局勢\((a_k,b_k)\),通過數學方法分析得到
\(a_k\)是未在前面出現過的最小自然數,而\(b_k=a_k+k\)

奇異局勢有以下三條性質:

  • 1.任何自然數都包含在一個且僅有一個奇異局勢中

證法一:
反證法,假設存在一個自然數\(n\)存在於兩個奇異局勢中,分別設為\((a,n),(b,n)\)其中\((a<b)\)
甲和乙進行博弈
假設甲拿到奇異局勢\((b,n)\),根據定義甲是必輸的
那麼甲就可以取\((b-a)\)個物品,讓乙面對奇異局勢\((a,n)\),這樣就是乙必輸,甲必贏
原本甲先拿到奇異局勢,就是甲必輸,但最後得出甲必勝,乙必輸,
這樣原來的奇異局勢\((b,n)\)

就是非奇異局勢,與原來的假設相互矛盾,
故不存在一個自然數\(n\)存在於兩個奇異局勢中,
即任何自然數都包含在一個且僅有一個奇異局勢中
證畢

證法二:
由於\(a_k\)是未在前面出現過的最小自然數,所以有\(ak > ak-1\) ,而 $$b_k= a_k + k> a_{k-1} + k-1 =b_{k-1}>a_{k-1}$$所以性質1成立

  • 2.任意操作都可將奇異局勢變為非奇異局勢

證明:
與性質一的證明類似,若存在一種取法,使得甲面對奇異局勢,轉換為乙面對奇異局勢,那麼由原先的甲必輸轉換為乙必輸,矛盾
故不存在一種取法,使得奇異局勢轉換為奇異局勢
即任意操作都可以將奇異局勢變為非奇異局勢
證畢

  • 3.採用適當的方法,可以將非奇異局勢變為奇異局勢。

這個很好理解,證明也很簡單分類討論就行,這裡就不加以贅述

威佐夫博奕的程式設計問題

威佐夫博奕主要有兩種問題

  1. 給定一個自然數,輸出對應的奇異局勢
  2. 給定一個局勢,判斷它是不是奇異局勢

問題一:打表實現,很簡單

問題二:
仔細觀察我們會發現,每種奇異局勢的第一個值(這裡假設第一堆數目小於第二堆的數目)
總是等於當前局勢的差值乘上1.618
我們都知道0.618是黃金分割率。而威佐夫博弈正好是1.618,這就是博弈的奇妙之處!
\(a_k=[(b_k-a_k)\times 1.618]\)(中括號代表向下取整)
C/C++中的double強制轉為int,就是向下取整,而並非是四捨五入
即a[k]=(int)((b[k]-a[k])*1.618));

當然這邊的1.618是近似值,程式設計題對精度eps要求極高
\(\displaystyle \frac{\sqrt{5} +1}{2} \approx 1.618\)
實際的值應該是約等號左邊的值

下面來看一下一道威佐夫博奕的題目,也是NOI的題目,hdu1527
題目連結

程式碼實現

#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	int a, b,tmp;
	double r, c;
	r = (sqrt(5) + 1) / 2;//為精確值
	while (~scanf("%d%d", &a, &b)) {
		//a存小的值,b存大的值
		if (a > b) {//若a>b則交換a和b
			a ^= b;
			b ^= a;
			a ^= b;
		}
		c = (double)(b - a);
		tmp = r * c;//double轉int,向下取整後的結果
		printf("%d\n", tmp != a);
		//關係表示式返回1或0
	}
}

本文參照:https://www.cnblogs.com/java20130726/archive/2013/05/24/3218207.html
https://blog.csdn.net/qq_41311604/article/details/79980882