SG函数的一些理解

前置技能:nim游戏。

任何一个博弈游戏我们都可以把它抽象为在一个有向无环图上的博弈。每个节点像其能转移的节点连一条有向边。

现在我们现在开始玩一个游戏来理解SG函数,n个石子每个人可以取走{1,3,4}个,不能取的人败。

那么我们把他抽象为一个图。每个节点x向(x-1)(x-3)(x-4)连一条有向边。这就变成一个有向无环图了。我们的游戏就变为。两个人轮流移动一个棋子,棋子的初始位置在n点,不能移动的人败。

这个比较好理解。然后我们定义mex(minimal excludant)运算。对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 定义SG[0]等于1即必败点。图如下

那么这题必胜点和必败点就可以很容易求出来了。

接下来我们把他扩展到多堆石子的情况。

在nim游戏中每堆石子的SG值就是这堆石子的数量。他的必败点和必胜点的确定就是n堆石子数量的异或和,本质就是SG函数的异或和。那么我们对于多堆石子的每次只能取{1,3,4}也是和nim一样处理。对每堆石子SG值异或得到答案即可。

那么贴上我在网上找的代码。

循环版:

int f[N],SG[N];
bool S[M];
void getSG(int n)
{
    memset(SG,0,sizeof(SG));
    for(int i=1;i<=n;i++)
    {
        memset(S,false,sizeof(S));
        for(int j=1;f[j]<=i&&j<M;j++)
        {
             S[SG[i-f[j]]]=true;
        }
        for(int j=0;;j++)
        if(!S[j])
        {
            SG[i]=j;
            break;
        }
    }
}

dfs版本

//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[110],sg[10010],n;
int SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return sg[x];
    bool vis[110];
    memset(vis,0,sizeof(vis));
    for(i=0;i<n;i++)
    {
        if(x>=s[i])
        {
            SG_dfs(x-s[i]);
            vis[sg[x-s[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            e=i;
            break;
        }
    return sg[x]=e;
}

猜你喜欢

转载自blog.csdn.net/qq_37632935/article/details/81512877
今日推荐