把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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_x\ge Fib_{i+1}\),所以如果先手不一次取完,且取的石头数\(\ge Fib_{i-2}\),显然后手可以一次取完。

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

考虑后手最后一次取最多也只能是取\(\frac23\times Fib_{i-2}\)个石头(即先手一次取走\(\frac13\times Fib_{i-2}\)个石头),而\(\frac 43\times 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;//斐波那契分解,答案是最小的斐波那契数
}
posted @ 2020-05-18 10:28  TheLostWeak  阅读(232)  评论(0编辑  收藏  举报