1. 程式人生 > >HDU 2516 取石子游戲(Fibonacci博弈)

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

問題描述

取石子游戲

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;
}