HDU 1848(SG函数应用)

原文链接:http://hi.baidu.com/king___haha/item/542a071140107f9598ce337c



fib[1..]={1,2,3,5,8,13,21,...};是菲波那契数列,fib[]<=1000
每次只能取 fib[i]个

1. 如果只有1堆m个,m是某个fib[i],m是必胜点,m=1,2,3,5,8,13,21,...是必胜点
易知0,4 是必败点.如果从m个中取走 k个(k=fib[i]) ,m-k是必败点
则 m是必胜点.

6-2=7-3=9-5=4是必败点, 6,7,9 是 必胜点

m=10,m-1=9,m-2=8,m-3=7,m-5=5,m-8=2

从m=10中所有取法后都是必胜点 ==> m=10是必败点


这样当只有1堆时我们可以求所有必败点,用 w[1001]表示,w[i]=0表示i是必败点
w[i]=1表示i是必胜点,
w[0]=0;w[1]=w[2]=w[3]=1;
for(i=4;i<=1000;i++)
{
   for(j=1;fib[j]<=i;j++) if(w[i-fib[j]==0){w[i]=1;break;}

//取所有的Fibonacci数都胜,则该点必败

   if(fib[j]>i)w[i]=0;
}

只有1堆时所有必败点:
0 4 10 14 20 24 30 36 40 46 50 56 60 66 72 76 82 86 92 96 102 108 112 118 122 128
132 138 150 160 169 176 186 192 196 202 206 212 218 222 228 232 238 242 248 254
260 264 270 274 280 284 290 296 300 306 310 316 322 326 332 338 342 348 352 358
364 368 374 378 384 388 394 400 406 410 416 420 426 430 436 442 446 452 456 462
468 472 478 484 488 494 498 504 510 514 520 524 530 534 540 552 556 562 566 572
576 582 588 592 598 602 608 618 635 644 662 672 688 694 698 704 708 714 723 730
734 740 750 754 766 772 776 782 791 798 808 814 818 824 830 834 840 844 850 854
860 866 872 876 882 886 892 896 902 908 912 918 922 928 934 938 944 950 954 960
964 970 976 980 986 990 996 1000


2. 有2堆m个和 n个

   如果 m和 n都是 (1堆的)必败点,则2堆 (m,n) 也是必败点
一般 有 必败+必败=必败, 这里+不是把2队合为1堆
          必败+必胜=必胜

          必胜+必胜=?不一定

例如 2堆   (4,10) 是必败

             (4, 5) 是必胜

    易知 ( m,m )必败


   定义: 如果 (m,n) 必败,则称 m和n等价
   等价是相等的推广:m=n,    m和n等价
   
   只有1堆时所有必败点都和0等价,我们说只有1堆时所有必败点是第0类的或他的等价类数是0;

   2堆 (1,5) 是必败的,5和1等价,和1等价的m叫做他的等价类数是1

   2堆时的情况用 w2[m1][m2] 表示,w2[m1][m2]=0表示 (m1,m2)必败,
   w2[m1][m2]=1表示 (m1,m2)必胜
   
   用前面方法可算出等价类数是1的数,例如 1,5,11的等价类数是1

   可以进行等价代换例如 3堆 1,5,7 可代换为 1,1,7,是必胜的


3. 结论(不证明了) 如果算出 m,n,p的等价类数是,E[m],E[n],E[p]

   令 s=E[m]^E[n]^E[p]


   则 s=0 是必败点


等价类数的算法
E[0]=0;


等价类数 E[i] 的算法:从i个中取走 fib[1],fib[2],...,fib[j]<=i   个后剩下

i-fib[1], i-fib[2],..., i-fib[j]个

他们的等价类数中没有出现的最小数就是i的等价类数

例如 i=1,取走fib[1]=1个 i-fib[1]=0,0的等价类数是0,没有出现的最小数就是1

E[1]=1;

例如 i=2,取走fib[1]=1个 i-fib[1]=1,取走fib[2]=2个 i-fib[1]=0,
   1和0的等价类数是1,0,没有出现的最小数就是2

E[2]=2;

例如 i=3,取走fib[1]=1个 i-fib[1]=2,取走fib[2]=2个 i-fib[1]=1,取走fib[3]=3个 i-fib[1]=0,
   2,1和0的等价类数是2,1,0,没有出现的最小数就是3

E[3]=3;

例如 i=4,取走fib[1,2,3]=1,2,3个 剩下3,2,1,没有出现的最小数就是0

E[4]=0;   4是必败点

例如 i=5,取走fib[1,2,3,4]=1,2,3,5个 剩下4,3,2,0,等价类数是 0,3,2,0没有出现的最小数就是1

E[5]=1;


//计算等价类数
E[0]=0;E[1]=1;
for(i=2;i<=1000;i++)
{ //首先假设 i<=1000时 等价类数<=15

for(j=0;j<=15;j++)h[j]=0;// h[j]==0, j没有出现,h[j]=1,j出现
for(j=1;fib[j]<=i;j++)
{
   h[ E[i-fib[j]] ]=1;

}
for(j=0;j<=15;j++)if(h[j]==0){E[i]=j;break;}//h[j]=0,j没有出现
}

因此计算步骤是:
1.计算菲波那契数列fib[i],计算到 fib[i]>1000为止
2.计算等价类数 E[i]
3. 同上面的 3

#include <iostream>
using namespace std;
int fib[1005],e[1005],h[20];
void Fib()//求Fibonacci数。
{
    fib[1]=1;
    fib[2]=2;
    for(int i=3; i<=16; i++)
        fib[i]=fib[i-1]+fib[i-2];
}
void E()
{
    e[0]=0;
    e[1]=1;
    int k;
    for(int i=2; i<=1000; i++)
    {
        for(int j=0; j<=15; j++) h[j]=0;//将H全部初始化为0
        for(int j=1; fib[j]<=i; j++)
            h[e[i-fib[j]]]=1;//标记读过的的数列
        /*
        i-Fib[j]为下一步可能到达的子局面或者说是可能出现的情况例如
        i=9时,出现的情况为{8,7,6,4,1} 
        E[i-Fib[j]]则表示此时的等价类数或者说是SG函数值 
        H[E[i-Fib[j]]]=1则是读过的数标记为1,要读的数为1~i 。
        接下来我们看下面一个for循环 
        */ 
        for(int j=0; j<=15; j++)
            if(!b[j])
            {
                e[i]=j;
                break;
            }
    }
}//求等价类数。
int main()
{
    int m,n,p;
    while(cin>>m>>n>>p)
    {
        if(m==0&&n==0&&p==0)break;
        Fib();
        E();
        int a=e[m],b=e[n],c=e[p];
        a=a^b^c;
        if((e[m]^e[n]^e[p])==0)
            cout<<"Nacci"<<endl;
        else
            cout<<"Fibo"<<endl;
    }
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值