• hdu2516斐波那契博弈


    刚开始想用sg函数做,想了半天没一点思路啊。

    原来这是一个新题型,斐波那契博弈

    斐波那契博弈模型:
    有一堆个数为 n 的石子,游戏双方轮流取石子,满足:
    1. 先手不能在第一次把所有的石子取完;
    2. 之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
    约定取走最后一个石子的人为赢家,求必败态。     

     n = 2时输出second;     
     n = 3时也是输出second; 
     n = 4时,第一个人想获胜就必须先拿1个,这时剩余的石子数为3,此时无论第二个人如何取,第一个人都能赢,输出first; 
     n = 5时,first不可能获胜,因为他取2时,second直接取掉剩下的3个就会获胜,当他取1时,这样就变成了n为4的情形,所以输出的是second;   
     n = 6时,first只要去掉1个,就可以让局势变成n为5的情形,所以输出的是first;      
     n = 7时,first取掉2个,局势变成n为5的情形,故first赢,所以输出的是first;     
     n = 8时,当first取1的时候,局势变为7的情形,第二个人可赢,first取2的时候,局势变成n为6得到情形,也是第二个人赢,取3的时候,second直接取掉剩下的5个,所以n = 8时,输出的是second;    
     …………      
     从上面的分析可以看出,n为2、3、5、8时,这些都是输出second,即必败点,仔细的人会发现这些满足斐波那契数的规律,可以推断13也是一个必败点。     
     借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。n=12时,只要谁能使石子剩下8且此次取子没超过3就能获胜。因此可以把12看成8+4,把8看成一个站,等价与对4进行"气喘操作"。又如13,13=8+5,5本来就是必败态,得出13也是必败态。也就是说,只要是斐波那契数,都是必败点。
    所以我们可以利用斐波那契数的公式:fib[i] = fib[i-1] + fib[i-2],只要n是斐波那契数就输出second
    证明转自:http://blog.csdn.net/dgq8211/article/details/7602807

    为了方便,我们将n记为f[i]。

    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时,结论依然成立。

    还有一点2^31 = 2 147 483 648,这个超过int范围了,所以用long long,fib到第46项即可

    #include<map>
    #include<set>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cassert>
    #include<iomanip>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define pi acos(-1)
    #define ll long long
    #define mod 1000000007
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #pragma comment(linker, "/STACK:1024000000,1024000000")
    
    using namespace std;
    
    const double g=10.0,eps=1e-9;
    const int N=1000+10,maxn=111117,inf=11111;
    
    int f[N],sg[N],Hash[N];
    void getsg(int n)
    {
        for(int i=1;i<=n;i++)
        {
            memset(Hash,0,sizeof Hash);
            for(int j=0;f[j]<=i;j++)
                Hash[sg[i-f[j]]]=1;
            for(int j=0;j<n;j++)
                if(!Hash[j])
                {
                    sg[i]=j;
                    break;
                }
        }
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        ll n;
        ll fib[N];
        fib[0]=fib[1]=1;
        for(int i=2;i<=46;i++)
            fib[i]=fib[i-1]+fib[i-2];
      //  cout<<fib[46]<<endl;
        while(cin>>n,n){
            bool flag=0;
            for(int i=1;i<=46;i++)
                if(n==fib[i])
                {
                   flag=1;
                   break;
                }
            if(flag)cout<<"Second win"<<endl;
            else cout<<"First win"<<endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    在程序中向水晶报表传参数,以及在程序中指定报表源
    运行Web程序时提示无法使用调试
    TreeView控件节点重命名后没有进入beginEdit的解决方案
    网络负载平衡(转)
    纵横表转交叉表
    重绘datagrid,包括强迫显示某行
    datagrid添加事件
    我的页面模板算法
    C++函数重载
    关于string.empty 与 "" 内存分配
  • 原文地址:https://www.cnblogs.com/acjiumeng/p/6962401.html
Copyright © 2020-2023  润新知