Fibonacci 博弈 HDU 2516 取石子游戏

题目规则

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值