题目规则
1 堆石子有 n 个,两人轮流取. 先取者第 1 次可以取任意多个,但不能全部取完. 以后每次取的石子数不能超过上次取子数的 2 倍。取完者胜.先取者 负 输出”Second win”.先取者 胜 输出”First win”.
2、解决思路:
当n为Fibonacci数时,先手必败。即存在先手的必败态当且仅当石头个数为Fibonacci数。
证明:
根据“Zeckendorf定理”(齐肯多夫定理):
任何正整数可以表示为若干个不连续的Fibonacci数之和。
如n = 83 = 55 + 21 + 5 + 2;
我们看看这个分解有什么指导意义:
假如先手取 2 颗,那么后手无法取 5 颗或更多,而 5 是一个Fibonacci数,那么一定是先手取走这 5 颗石子中的最后一颗,同理,接下去先手取走接下来的后 21 颗中的最后一颗,再取走后 55 颗中的最后一颗,那么先手赢。
反之:如果n是Fibonacci数,如 n = 89 :
记先手一开始所取的石子数为y
(1)若y >= 34 颗(也就是 89 的向前两项),那么一定后手赢,因为89-34 = 55 = 34+21<2*34。
(2)y < 34时剩下的石子数 x 介于 55到 89 之间,它一定不是一个 Fibonacci 数,把 x 分解成Fibonacci数:x = 55 + f[i] + … + f[j],如果f[j]<=2y,那么对B就是面临x局面的先手,所以根据之前的分析,后手只要先取f[j]个即可,以后再按之前的分析就可保证必胜。
题目链接:HDU 2516
附上代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<iomanip>
#include<cmath>
using namespace std;
long long a[50],n;
const long long maxn = 4294967295;
int main()
{
a[1] = 1;
a[2] = 2;
for(int i = 3; i < 50; i++){
a[i] = a[i - 1] + a[i - 2];
}
while(scanf("%lld",&n)!=EOF&&n)
{
bool f = false;
for(int i = 1; i < 50; i++)
if(a[i] == n) f = true;
if(f) puts("Second win");
else puts("First win");
}
return 0;
}