nyoj-358 取石子(五)(斐波那契博弈)

取石子(五)

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述
himdd最近很想玩游戏,于是他找到acmj和他一起玩,游戏是这样的:有一堆石子,两个人轮流从其中取走一定的石子,取走最后所有石子的人为赢家,不过得遵循如下规则:
1.第一次取不能取完,至少取1颗.

2.从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。

himdd事先想知道自己会不会赢,你能帮帮他吗?(每次himdd先手)

输入
有多组测试数据,每组有一个整数n(2<=n<2^64);
输出
himdd会赢输出Yes,否则输出No;
样例输入
2
5
6
样例输出
No
No
Yes


思路:

      结论:当n为fibonacci数时,先手必败(n >= 2);

      证明:

      参考资料:http://blog.csdn.net/dgq8211/article/details/7602807

      以下为个人思路:

      根据“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和(优先选取最大的)。

      比如 n = 14 时, n = 8 + 5 + 1;而先手要赢的条件是,先手必须要先取完最小的一堆,再取完次小的一堆,以此类推,并保证每堆的最后一个是自己取到的而且不能取超!听起来有点绕,举个例子:

      1、先看必胜态,n不是fibonacci数的时候,令n = 9 =5 + 3 + 1:

            a、先手先取最小的一堆,1个,保证了最后一个是自己取得;

            b、此时后手开始取次小的一堆(3个石子),上面说过,要想赢的话,要保证这一堆的第3个必须是自己取得;此时后手有 1个、2个两种取法,但此处无论后手怎么取,都能保证这一堆的最后一个(第3个)是自己取得;

            c、此时后手开始取最后一堆(5个石子),b步先手可能取得是1或2,因此此步后手可能有1、2、3、4四种取法,易知后手取3或4的话,自己立马就能取到最后一个,就赢了,而取1或2时,同上(读者自己来),仍能保证取到最后一个(此堆第5个,全局最后一个),必赢!

      2、再说必败态,若n本身是fibonacci数时,已经不需要分解了,为方便观察,令n=5:

      实际上与上面相同,每一堆(有fib个石子)在自己先手的情况下,无论怎么取,后手都能保证自己取到这一堆的最后一个,因此后手必胜!

  PS:所以上面说总石子数为非fibonacci数时先手必胜的原因在于,总数目分解为若干fib数时,先手先全部取走最小的一堆1个(也是取到了这一堆的最后一个),以后每堆(有fib[i]个石子)都是后手先取,先手便有把握取到这一堆的最后一个了!


代码:

#include <stdio.h>
#define N 100

long long fib[N];

void Fib()
{
	fib[0] = 0;
	fib[1] = 1;
	for(int i = 2; i < N; i ++){
		fib[i] = fib[i - 1] + fib[i - 2];
	}
}

int main()
{
	Fib();

	long long n;
	while(scanf("%lld", &n) != EOF){
		bool is_fib = 0;
		for(int i = 2; i < 100; i ++){
			if(fib[i] == n){
				is_fib = 1;
				break;
			}
		}

		if(is_fib)
			printf("No\n");
		else
			printf("Yes\n");
	}

	return 0;
}







  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值