zoukankan      html  css  js  c++  java
  • 斐波那契博弈

    处理何种问题:有一堆个数为n的石子,游戏双方轮流取石子,满足:

    1)    先手不能在第一次把所有的石子取完,且最少取1个;

    2)    之后每次可以取得石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取得石子数的两倍)。

    约定取走最后一个石子的人为赢家,求谁获胜。

    此类问题有一个结论:当n是斐波那契数时,后手必胜;当n不是斐波那契数时,先手必胜。(先手胜当且仅当n不是斐波那契数)

    性能:O(1)的时间复杂度

    原理:本人对该类型证明不是很感兴趣,再此贴一份证明

    这里需要借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。

    先看看FIB数列的必败证明:

    1、当i=2时,先手只能取1颗,显然必败,结论成立。

    2、假设当i<=k时,结论成立。

    则当i=k+1时,f[i] = f[k]+f[k-1]。

    则我们可以把这一堆石子看成两堆,简称k堆和k-1堆。

    (一定可以看成两堆,因为假如先手第一次取的石子数大于或等于f[k-1],则后手可以直接取完f[k],因为f[k] < 2*f[k-1])

    对于k-1堆,由假设可知,不论先手怎样取,后手总能取到最后一颗。下面我们分析一下后手最后取的石子数x的情况。

    如果先手第一次取的石子数y>=f[k-1]/3,则这小堆所剩的石子数小于2y,即后手可以直接取完,此时x=f[k-1]-y,则x<=2/3*f[k-1]。

    我们来比较一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,由数学归纳法不难得出,后者大。

    所以我们得到,x<1/2*f[k]。

    即后手取完k-1堆后,先手不能一下取完k堆,所以游戏规则没有改变,则由假设可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。

    即i=k+1时,结论依然成立。

    对于不是FIB数,首先进行分解。

    分解的时候,要取尽量大的Fibonacci数。

    比如分解85:85在55和89之间,于是可以写成85=55+30,然后继续分解30,30在21和34之间,所以可以写成30=21+9,

    依此类推,最后分解成85=55+21+8+1。

    则我们可以把n写成  n = f[a1]+f[a2]+……+f[ap]。(a1>a2>……>ap)

    我们令先手先取完f[ap],即最小的这一堆。由于各个f之间不连续,则a(p-1) > ap  + 1,则有f[a(p-1)] > 2*f[ap]。即后手只能取f[a(p-1)]这一堆,且不能一次取完。

    此时后手相当于面临这个子游戏(只有f[a(p-1)]这一堆石子,且后手先取)的必败态,即先手一定可以取到这一堆的最后一颗石子。

    同理可知,对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。

    实现步骤:斐波那契数列+二分查找

    备注:记好结论

    输入样例解释

    2 //n,石子个数

    13

    10000

    0//结束语句

    输出样例解释

    Second win //后手获胜

    Second win

    First win //先手获胜

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    long long fib[50];
    
    int main()
    {
        fib[0]=fib[1]=1;
        for(int i=2;i<50;++i)//n的个数一定要在斐波那契数列之内
        {
            fib[i]=fib[i-1]+fib[i-2];
        }
    
        long long n;
    
        while(scanf("%lld",&n)&&n)
        {
            int flag=binary_search(fib,fib+50,n);
            if(flag)
                printf("Second win
    ");
            else
                printf("First win
    ");
    
        }
        return 0;
    }
    

      

  • 相关阅读:
    dubbo入门(一)
    java中文件操作《一》
    Unity 游戏框架搭建 2019 (七) 自定义快捷键
    凉鞋:我所理解的框架 【Unity 游戏框架搭建】
    Unity 游戏框架搭建 2019 (六) MenuItem 复用
    Unity 游戏框架搭建 2019 (五) 打开所在文件夹
    Unity 游戏框架搭建 2019 (四) 导出 UnityPackage
    Unity 游戏框架搭建 2019 (三) 生成文件名到剪切板
    Unity 游戏框架搭建 2019 (二) 文本复制到剪切板
    Unity 游戏框架搭建 2019 (一) 简介与第一个示例文件名的生成
  • 原文地址:https://www.cnblogs.com/l1l1/p/9477797.html
Copyright © 2011-2022 走看看