POJ 1067取石子游戏


取石子游戏
Time Limit: 1000MS	
Memory Limit: 10000K
Total Submissions: 34108	
Accepted: 11373
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
0
Source
NOI
[Submit]   [Go Back]   [Status]   [Discuss]
大致上是这样的:有两堆石子,不妨先认为一堆有10,另一堆有15个,双方轮流取走一些石子,合法的取法有如下两种:
1)在一堆石子中取走任意多颗;

2)在两堆石子中取走相同多的任意颗;

约定取走最后一颗石子的人为赢家,求必败态(必胜策略)。

这个可以说是MR.Wythoff(Wythoff于1907年提出此游戏)一生全部的贡献吧,我在一篇日志里就说完有点残酷。这个问题好像被用作编程竞赛的题目,网上有很多把它Label为POJ1067,不过如果学编程的人不知道Beatty定理和Beatty序列 ,他们所做的只能是找规律而已。不熟悉的人可以先在这里 玩几局~

简单分析一下,容易知道两堆石头地位是一样的,我们用余下的石子数(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数;而且两者的比值也越来越接近黄金比,这些性质在得到通项公式之后不难证明。


//再继续看看必败点

一、
      m(k) = k * (1 + sqrt(5))/2(k为差值)
      n(k) = m(k) + k
 二、
 
一个必败点有如下性质:
性质1:所有自然数都会出现在一个必败点中,且仅会出现在一个必败点中;
性质2:规则允许的任意操作可将必败点移动到必胜点;
性质3:一定存在规则允许的某种操作可将必胜点移动到必败点;
 
下面我们证明这3个性质。
 
性质1:所有自然数都会出现在一个必败点中,且仅会出现在一个必败点中;
证明:m(k)是前面没有出现过的最小自然数,自然与前k-1个必败点中的数字都不同;m(k)>m(k-1),否则违背m(k-1)的选择原则;n(k)=m(k)+k>m(k-1)+(k-1)=n(k-1)>m(k-1),因此n(k)比以往出现的任何数都大,即也没有出现过。又由于m(k)的选择原则,所有自然数都会出现在某个必败点中。
 
 
性质2:规则允许的任意操作可将必败点移动到必胜点;
证明:以必败点(m(k),n(k))为例。若只改变两个数中的一个,由于性质1,则得到的点一定是必胜点;若同时增加两个数,由于不能改变两数之差,又有n(k)-m(k)=k,故得到的点也一定是必胜点。
 
 
性质3:一定存在规则允许的某种操作可将必胜点移动到必败点;
证明:以某个必胜点(i,j)为例,其中j>i。因为所有自然数都会出现在某个必败点中,故要么i等于m(k),要么j等于n(k)。
若i=m(k),j>n(k),可从j中取走j-n(k)个石子到达必败点;
若i=m(k),j<n(k),可从两堆同时拿走m(k)-m(j-m(k)),注意此时j-m(k) < n(k)-m(k) < k,从而到达必败点( m(j-m(k)),m(j-m(k))+j-m(k));
若i>m(k),j=n(k),可从i中取走i-m(k)个石子到达必败点;
若i<m(k),j=n(k),需要再分两种情况,因为i一定也出现在某个必败点中,若i=m(l),则从j中拿走j-n(l),若i=n(l),则从j中拿走j-m(l),从而到达必败点(m(l),n(l))。
代码如下:
#include<iostream>
#include<cmath>
using namespace std;
int main ()
{
    int a,b,dif;
    double p=(sqrt((double)5)+1)/double(2);
    while(cin>>a>>b)
    {
    dif=abs(a-b);  //取差值
    a=a<b?a:b;    //取较小的值
    if(a==(int)(p*dif))//判断是不是奇异局势
      printf("0\n");
    else printf("1\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值