博弈论中的SG函数

SG函数的定义:

g(x) = mex ( sg(y) |y是x的后继结点 )

其中mex(x)(x是一个自然是集合)函数是x关于自然数集合的补集中的最小值,比如x={0,1,2,4,6} 则mex(x)=3;

什么是后继结点?

所谓后继结点就是当前结点经过一个操作可以变成的状态。比如对于娶4石子游戏,假如每次可以取的数目是1,2,4,当前的石子数目也就是当前状态是5,那么5的后继结点就是{5-1, 5-2, 5-4}={4,3,1};

如果5的三个后继结点的SG函数值分别为0,1,3,那么5的SG值就是集合{0,1,3}的补集的最小元素,也就是2。

关于整个游戏的sg值之和sum,定义sum=sg1 ^ sg2 ^ sg3 ^ ……sgn.  其中^表示按位异或运算。

结论:一个游戏的初始局面是必败态当且仅当sum=0。

一篇非常好的关于SG值的论文:http://www.cnitblog.com/weiweibbs/articles/42735.html

SG值打表模板:

//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//hash[]:mex{}
int f[N],sg[N],hash[N];     
void getSG(int n)
{
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1;i<=n;i++)
    {
        memset(hash,0,sizeof(hash));
        for(j=1;f[j]<=i;j++)
            hash[sg[i-f[j]]]=1;
        for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
        {
            if(hash[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}

HDU1848  

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1848

题意:取石子问题,一共有3堆石子,每次只能取斐波那契数个石子,先取完石子者胜利,问先手胜还是后手胜

  1. 可选步数为一系列不连续的数,用GetSG(计算) 
  2. 最终结果是所有SG值异或的结果 
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1001;
//f[]  可以取走的石子数
//sg[]   0~n的sg函数值
//hash[]  mex{}
int f[maxn],sg[maxn],hash[maxn];
void getsg(int n)
{
    memset(sg,0,sizeof(sg));
    for(int i=1;i<=n;i++)
    {
        memset(hash,0,sizeof(hash));
        for(int j=1;f[j]<=i;j++)
            hash[sg[i-f[j]]]=1;
        for(int j=0;j<=n;j++)
        {
            if(hash[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}
int main()
{
    int n,m,k;
    f[0]=f[1]=1;
        for(int i=2;i<=16;i++)
            f[i]=f[i-1]+f[i-2];
        getsg(1000);
    while(cin>>n>>m>>k)
    {
        if(!n&&!m&&!k)
            break;
        int sum=0;
        sum=sg[n]^sg[m]^sg[k];
        if(sum==0)
            cout<<"Nacci"<<endl;
        else
            cout<<"Fibo"<<endl;
    }
    return 0;
}



原文地址:https://www.cnblogs.com/wolf940509/p/6617117.html