威佐夫博奕

威佐夫博奕

威佐夫博奕(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

posted @ 2020-10-25 14:31  幽灵轩  阅读(142)  评论(0编辑  收藏  举报