HDU 2516 取石子游戏(Fibonacci博弈)

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2516

问题描述

取石子游戏

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8263    Accepted Submission(s): 5021


 

Problem Description

1堆石子有n,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".

 

 

Input

输入有多组.每组第1行是2<=n<2^31. n=0退出.

 

 

Output

先取者负输出"Second win". 先取者胜输出"First win". 
参看Sample Output.

 

 

Sample Input

2

13

10000

0

 

 

Sample Output

Second win

Second win

First win

------------------------------------------------------------

思路

斐波那契博弈,基于Zeckendorf定理

定理1.(Zeckendorf定理):任意一个正整数都能分解成若干个不连续的Fibonacci数之和。(证明略)

首先给出结论:后手必胜的充要条件是n为Fibonacci数

定理2.(充分性的证明)

数学归纳法。n = 2,先手只能取1个,后手胜。

对于任意n’> 2, n’= Fib(k) = Fib(k-1) + Fib(k-2), Fib(k-1) < 2 * Fib(k-2). 如果先手取的个数大于等于Fib(k-2),则后手一次可以取完余下所有,后手胜。故只需考虑先手取x个,x = 1,2,…,Fib(k-2)-1个的情形。此时后手的策略是取y = Fib(k-2)-x个,y <= Fib(k-2) – 1,即取完Fib(k-2), 令问题演化为先手面对Fib(k-1)的问题。显然 y < 1/2 * Fib(k-1),先手面对Fib(k-1)时,第一次可以取1,2,…,2*y < Fib(k-1)个,这是Fib(k-1)时原问题的弱问题(原问题为先手第一次可以取1,2,…,Fib(k-1)-1个),由归纳法知后手胜。

由此,我们归纳证明了n为Fibonacci数时后手必胜。

定理3.(必要性的证明,即证若n不是Fibonacci数,则先手必胜):

由Zeckendorf定理,给定正整数n,有 n = Fib(a[1]) + Fib(a[2]) + … + Fib(a[k]), a[1] > a[2] + 1 > a[3] + 2 > … > a[k] + k-1. 当k > 1时,先手的必胜策略为第一次拿走Fib(ak), 那么根据(1) Fibonacci数列的性质 以及(2) a[k-1] > a[k]+1, 可得 Fib(a[k-1]) > 2 * Fib(a[k]).此时先手的策略为从n个中取走Fib(a[k])个,这样问题演化为后手面对 n’= Fib(a[1]) + Fib(a[2]) + … + Fib(a[k-1])且后手第一次不能取完Fib(a[k-1]). 由定理2,该问题可以演化为k-1个后手面对Fib(a[u]), u=1,2,…,k-1的“先手必胜”的子问题,即先手可以依次取完Fib(a[k-1]), Fib(a[k-2]), …, Fib(a[1]),从而先手会取走最后一个,先手必胜。

------------------------------------------------------------

代码

#include<cstdio>

bool is_fib(int n) // 判断n是否是斐波那契数列中的元素(n>=2)
{
	int i = 2, j = 3, k;
	while (n >= i)
	{
		if (n == i || n == j)
		{
			return true;
		}
		else if (i < n && n < j)
		{
			return false;
		}
		else
		{
			k = j;
			j += i;
			i = k;
		}
	}
	return false;
}

int main()
{
	int n;
	while (scanf("%d", &n))
	{
		if (n == 0)
		{
			break;
		}
		printf(is_fib(n) ? "Second win\n" : "First win\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/82945862