首页 > 代码库 > [原博客] POJ 1067 取石子游戏

[原博客] POJ 1067 取石子游戏

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

但是这道题实在是呵呵。
开始没啥思路,根据必胜状态必败状态的定义,n^3打了个表,看起来是这样的。

图为100x100,已经缩小,左上角是状态(0,0),右下角状态为(100,100),黄色标出的是必败状态。(哇,博客还能传图,真好~)
嗯,对称是显然的吧,因为这两堆可以直接交换,而且看起来很有规律的样子。
T_T找不到规律。

后来知道这个叫“威佐夫博奕(Wythoff Game)”。百科
这个问题中必败状态叫奇异局势(奇异~),然后可以有公式去算,好像还与黄金分割有半毛钱关系,具体看百科证明吧。

规律摘抄如下:

  • 任何自然数都包含在一个且仅有一个奇异局势中。
  • 任意操作都可将奇异局势变为非奇异局势。(必败状态)
  • 采用适当的方法,可以将非奇异局势变为奇异局势。(必胜状态)
  • 如果用(ak,bk)表示一个状态,设ak<=bk,则有a0=b0=0,ak是未在前面出现过的最小自然数,且bk=ak+k

公式ak =[k(1+√5)/2],bk= ak + k(k=0,1,2,...,n 方括号表示取整函数)。
做法:如果对于(a,b)是奇异局势的话,应该有k=b-a,然后根据k计算出a等不等于ak即可,若相等,该状态为奇异局势,必败输出0,否则输出1。
*涨姿势。

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>//by zrt//problem:using namespace std;typedef long long LL;const int inf(0x3f3f3f3f);const double eps(1e-9); int main(){    #ifdef LOCAL    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);    #endif    double sqrt5=sqrt(5.0);    int a,b;    while(~scanf("%d%d",&a,&b)){        if(a>b) swap(a,b);        int j=b-a;        int tmp=(int)(j*(1+sqrt5)/2.0);        if(tmp==a) puts("0");        else puts("1");    }    return 0;}
View Code

 

[原博客] POJ 1067 取石子游戏