其他分享
首页 > 其他分享> > 博弈 SG打表

博弈 SG打表

作者:互联网

Nim游戏

一共有N堆石子,编号1…n,第i堆中有个a[i]个石子。
每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。
两个人轮流行动,取走最后一个的人胜利。Alice为先手。

对于该博弈,我们知道

a[1] xor a[2] xor a[3]…xor a[n]==0
先手必败
反之 先手必胜

SG函数(打表)

sg[n]代表对于一堆只有n个的石子,两人轮流拿,若sg[n]==0,则先手必败,反之,必胜
首先易知 sg[0]=0,没有石子,先手必败
随后,对于sg[n]
s g [ n ] = m e x ( s g [ n − 1 ] , s g [ n − 2 ] , . . . ) sg[n]=mex(sg[n-1],sg[n-2],...) sg[n]=mex(sg[n−1],sg[n−2],...)
mex括号内为所有可以拿取的石子数量

int sg[maxn];//sg[n] n表示每堆数量
int s[k];//每次能取的值,下标从0开始,0 ~ k-1,必须有序,可以sort(s,s+k);
bool vis[maxn];
const int k;//k是集合s的大小 
void getsg()
{
    int i,j;
    for(i=0;i<maxn;i++)
    {
        memset(vis,0,sizeof(vis));
        j=0;
        while(j<k&&s[j]<=i)
        {
          vis[sg[i-s[j]]]=1;
        j++;
        }
        for(j=0;j<maxn;j++)
        if(!vis[j])
        {
            sg[i]=j;
            break;
        }
    }
}
int main()
{
    ...
    memset(sg,-1,sizeof(sg));
    getsg();
    if(sg[n]==0) //先手必败
    else    //先手必胜

   //如果有多堆,则
   // num=sg[n1]^sg[n2]^sg[n3]^....^sg[nx];
   // if(num==0) 则先手必败
   // else    先手必胜
    ...
}

对于多堆石子的情况
我们将得到的sg函数
进行xor运算
再次进行判断即可。

阶梯博弈

对于树,1号为根节点。
每个节点都有石子,每次操作可以由一个节点,移动任意数量石子,到该点的父节点,Alice和Bob轮流进行操作,最终无法操作(所有石子都在节点1)的人输掉
在这里插入图片描述
对于该树
若Alice将第三层的任何值移动到第二层,Bob随即便可以将其顺势移动到第一层。如此,变对胜负完全没有影响
由此我们发现,移动奇数层的节点没有意义
奇数层的节点等同于根节点
所以我们可以将该模型理解为,所有偶数层节点的一堆石子进行的经典Nim游戏

标签:博弈,int,sg,石子,Alice,打表,xor,SG,节点
来源: https://blog.csdn.net/zppbugmaker/article/details/118058176