为了账号安全,请及时绑定邮箱和手机立即绑定

博弈——威佐夫博弈(hdu1527,2177)

标签:
算法

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1527

题目描述:

有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。


首先介绍一下威佐夫博弈(Wythoff Game)的概念:

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

性质:两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。

判断是否为奇异局势:任给一个局势(a,b), (下面用到的a是两者中较小的数)ak =[k(1+√5)/2],bk= ak + k  (k=0,1,2,…,n 方括号表示取整函数)

奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618…,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],

若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,

若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。


我们再用具体的数值说明一下:

当面对(0,0)时很显然我们是处于必败的状态,而下一个该状态即(1,2)

因为无论我们做(0,2)(1,1)(1,0)那种操作对方都能使我们变成(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……

所以第一个值 = 差值 * 1.618满足黄金分割数的比例。


这是为什么呢?

首先,若 (x, y) 是必败态,那么(x+i,y),((x,y+i)肯定是必胜态,因为我可以通过拿去i个石子使其变成(x,y)

这也就说明了,以后的必败态绝对不会再出现x、y。因为如果出现,我可以通过改变另一个值使它变为(x,y),那么就不是必败态了。

首先,假设我们得到状态 (a[i] - p, a[i] - p + y),并且比 a[i] 小的数都在之前出现过,若 a[i] - p 存在必败态 ,那么当前状态 (a[i] - p, a[i] - p + y)肯定是必胜态了

若 a[i] - p 出现在较大的数,由于小数是单增的,因而其对应的小数必小于 a[i] + y,故而也可推出其状态为必胜态。

于是,(a[i], a[i] +y) 状态的胜负性只与状态 (a[i], a[i] + d) (d <y) 有关。不难看出,y= i 时恰为必败态,因为不论从第二堆中取出多少个石子,作为另一堆的第一堆石子并没有在之

前出现过,所以得到的一定是一个必胜态,因而 (a[i], a[i] + y) 为必败态,即矩阵i 个必败态大数等于小数加上 i。

所以差值就是0,1,2,3,4,5,6,7……了



#include<stdio.h>#include<math.h>int main(){int a,b;while(~scanf("%d%d",&a,&b)){double j,k,r;int t;if(a>b){t=a;a=b;b=t;}r=(sqrt(5.0)-1)/2;j=(int)(r*a);if(a!=(int)(j/r))j+=1;if(b!=(int)(j/r)+j)printf("1\n");elseprintf("0\n");}return 0;}


题目链接:


http://acm.hdu.edu.cn/showproblem.php?pid=2177

原理相同



#include<stdio.h>#include<math.h>int main(){	int a,b;	while(~scanf("%d%d",&a,&b)&&(a||b))	{		double j,k,r,x;		int i,n,m,m_n;		int t;		if(a>b)		{			t=a;			a=b;			b=t;		}				r=(sqrt(5.0)-1)/2;		x=r+1;		j=(int)(r*a);		if(a!=(int)(j/r))		j+=1;		if(b==(int)(j/r)+j)		printf("0\n");		else		{		printf("1\n");		for(i=1;i<=a;i++){n=a-i;m=b-i;m_n=m-n;if((int)(m_n*x)==n)printf("%d %d\n",n,m);}for(i=b-1;i>=0;i--){n=a;m=i;if(n>m){t=n;n=m;m=t;}m_n=m-n;if((int)(m_n*x)==n)printf("%d %d\n",n,m);}}		}	return 0;}


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消