【BZOJ2275】[COCI2010] HRPA(斐波那契博弈)

点此看题面

大致题意:(n)个石头,每次取的石头数量不能超过另一个人前次取的石头数量的两倍,取到最后一个石头赢。问先手第一次至少取多少石头,才能保证必胜。

前言

稀奇古怪的博弈论题。。。

说实话我并不知道斐波那契博弈除了这道板子题以外还能干什么。。。

斐波那契博弈

对于这道题,有这样一个结论:对于一个斐波那契数,先手必须一次取完,否则必输。

考虑用归纳思想证明。

对于(Fib_1=1)(Fib_2=2)的情况,这个结论显然成立。

否则对于第(i)个斐波那契数(Fib_i=Fib_{i-2}+Fib_{i-1}),假设我们已经证明这一结论对于(Fib_{i-2})(Fib_{i-1})成立。

而斐波那契数满足性质(2Fib_xge Fib_{i+1}),所以如果先手不一次取完,且取的石头数(ge Fib_{i-2}),显然后手可以一次取完。

因此先手不能一次取完(Fib_{i-2})。而我们把(Fib_{i-2})(Fib_{i-1})分别看作两堆石头,则根据对(Fib_{i-2})已经证明的这一结论,后手必然可以取到(Fib_{i-2})这堆中的最后一个石头。

考虑后手最后一次取最多也只能是取(frac23 imes Fib_{i-2})个石头(即先手一次取走(frac13 imes Fib_{i-2})个石头),而(frac 43 imes Fib_{i-2})显然是小于(Fib_{i-1})的。

这也就意味着先手不能一次取完(Fib_{i-1})这一堆,而根据对(Fib_{i-1})已经证明的这一结论,后手可以取到(Fib_{i-1})这一堆的最后一个石头,也就是(Fib_i)中的最后一个石头。

于是这个结论就得证了。

而我们考虑一个非斐波那契数,我们可以把它分解成若干个非连续的斐波那契数(因为连续的斐波那契数(Fib_{i-2},Fib_{i-1})可以拼成(Fib_i))的和。

此时先手怎样才能赢呢?

由于斐波那契数的性质(2Fib_x<Fib_{x+2}),对于这些非连续的斐波那契数,一次取完较小的一堆后,另一个人无法一次取完较大的一堆。

所以,先手只要取走最小的斐波那契数,就能让自己成为后手,然后从小到大依次取完每一堆,就必胜了。

而具体代码实现是非常简单的。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define LL long long
using namespace std;
LL n,Fib[60];
int main()
{
	RI i;for(scanf("%lld",&n),Fib[1]=1,Fib[2]=2,i=3;(Fib[i]=Fib[i-2]+Fib[i-1])<n;++i);//求斐波那契数
	W(n^Fib[i]) n>Fib[i]&&(n-=Fib[i]),--i;return printf("%d",n),0;//斐波那契分解,答案是最小的斐波那契数
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2275.html