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