HDU 1848 浅谈组合游戏博弈论及SG函数MEX推导SG定理

HDU 1848 浅谈组合游戏博弈论及SG函数MEX推导SG定理_第1张图片
世界真的很大
今天考试考了博弈论,发现对于博弈论除了原版的nim游戏之外就什么都不会了
对于作为其基础的SG函数及定理也是完全不了解,这样是不得行的
要通过不断地刷题,总结,得出一套博弈论的题的做题思路才好
看题先:

description:

任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的: 
F(1)=1; 
F(2)=2; 
F(n)=F(n-1)+F(n-2)(n>=3); 
所以,1,2,3,5,8,13……就是菲波那契数列。 
在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。 
今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下: 
1、  这是一个二人游戏; 
2、  一共有3堆石子,数量分别是m, n, p个; 
3、  两人轮流走; 
4、  每走一步可以选择任意一堆石子,然后取走f个; 
5、  f只能是菲波那契数列中的元素(即每次只能取12358…等数量); 
6、  最先取光所有石子的人为胜者; 

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。 

input:

输入数据包含多个测试用例,每个测试用例占一行,包含3个整数m,n,p1<=m,n,p<=1000)。 
m=n=p=0则表示输入结束。 

output:

如果先手的人能赢,请输出“Fibo”,否则请输出“Nacci”,
每个实例的输出占一行。

对于这种一看就是博弈论的题的题,思路就没法总结了
就总结一下这样的博弈论问题的总结思路吧
首先确定这样的博弈游戏属不属于组合游戏的范畴:
1、有且仅有两个玩家 2、游戏双方轮流操作 3、游戏操作状态是个有限的集合(比如:取石子游戏,石子是有限的,棋盘中的棋盘大小的有限的) 4、游戏必须在有限次内结束 5、当一方无法操作时,游戏结束。

只要确定了其属于组合游戏,就可以采用组合游戏的特殊解法SG定理了
SG定理就是一个组合游戏,若是由许多个小游戏,或者说小的部分组成的,那么大游戏的SG函数值就是所有的小游戏的函数值的异或和,当大游戏的SG函数值为0时,先手必败,反之先手必胜
当然结论建立在双方都足够聪明的基础上
现在来谈谈上文的SG函数值是一个什么东西
以nim游戏举例,首先规定边界条件,当只有0个石子时当然先手必败,所以SG(0)=0
设一堆石子的大小为x,我们想要得到的自然就是SG(x)
由定理得一个游戏(状态)的SG函数值可以由其子游戏(后继状态)转移而来
考虑x能转移到哪些后继状态,由于nim游戏可以取任意颗石子,所以可以转移到x-1到1的所有状态
那么我们定义一种运算mex,定义SG(x)=mex(SG(x的所有后继状态))
mex运算就是取其中最小的没有出现的非负整数
考虑nim游戏中的SG(1),其只能转移到SG(0),而SG(0)的值为0,那么SG(1)的值就是1了。
以此类推SG(i)函数的值就是i了

凡是组合游戏就可以考虑SG函数和SG定理来考虑
这道题也是如此

考虑这道题的SG函数,SG(x)表示一堆数量为x的石子的SG函数,由于每次可以取走的石子的数量是确定的,所以每一堆石子的后继状态就是确定的
设f(i)表示第i个斐波拉契数,那么SG(x)=mex(SG(x-f(i)))
可以用打标记的方法得到mex的值,还是比较简单

之说以我只是在阐释SG定理和函数的意义,而完全没有证明
那是因为我也不会。。。

完整代码:

#include
#include
using namespace std;

int a,b,c,tot=0,SG[1010],book[1010],f[1010];
void init(int n)
{
    f[0]=1,f[1]=1,tot=1;
    while(f[tot]<=n)
    {
        tot++;
        f[tot]=f[tot-1]+f[tot-2];
    }
    SG[0]=0;
    for(int i=1;i<=n;i++)
    {
        memset(book,0,sizeof(book));
        for(int j=1;f[j]<=i;j++)
            book[SG[i-f[j]]]=1;
        for(int j=0;j<=i;j++)
            if(book[j]==0)
            {
                SG[i]=j; 
                break ;
            } 
    }
}

int main()
{
    init(1005);
    while(1)
    {
        scanf("%d%d%d",&a,&b,&c);
        if(!a && !b && !c) break ;
        int ans=SG[a]^SG[b]^SG[c];
        if(ans) printf("Fibo\n");
        else printf("Nacci\n");
    }
    return 0;
}
/*
EL PSY CONGROO
*/

你可能感兴趣的:(奇妙题,博弈论)