您的位置:首页 > 其它

Fibonacci 博弈 HDU 2516 取石子游戏

2018-01-21 21:39 330 查看
题目规则

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: