zoukankan      html  css  js  c++  java
  • 【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;//斐波那契分解,答案是最小的斐波那契数
    }
    
  • 相关阅读:
    Java九种基本数据类型的大小,以及他们的封装类
    php之正则表达式
    代码开发注意事项
    提后端需求的要求
    后端上线规范
    关注公众号的微信用户收到非本人操作的充值消费记录,故障记录
    软件三层架构和MVC模式的区别
    HttpClient发送请求和接收参数
    js页面倒计时
    小程序换取openId
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2275.html
Copyright © 2011-2022 走看看