知识点 - 博弈SG函数

知识点 - SG函数

解决问题类型:

​ Impartial Combinatorial Games问题

概要

摘自https://blog.csdn.net/strangedbly/article/details/51137432

​ 在有向无环图的顶点上定义Sprague-Garundy函数。

​ 首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

​ 对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g如下:g(x)=mex{ g(y) | y是x的后继 }。

​ 来看一下SG函数的性质。首先,所有的terminal position所对应的顶点,也就是没有出边的顶点,其SG值为0,因为它的后继集合是空集。

​ 对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0。 N

​ 对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0。 P

​ 以上这三句话表明,顶点x所代表的postion是P-position当且仅当g(x)=0(跟P-positioin/N-position的定义的那三句话是完全对应的)。我们通过计算有向无环图的每个顶点的SG值,就可以对每种局面找到必胜策略了。但SG函数的用途远没有这样简单。

​ 如果将有向图游戏变复杂一点,比如说,有向图上并不是只有一枚棋子,而是有n枚棋子,每次可以任选一颗进行移动,让我们再来考虑一下顶点的SG值的意义。当g(x)=k时,表明对于任意一个0<=i<k,都存在x的一个后继y满足g(y)=i。也就是说,当某枚棋子的SG值是k时,我们可以把它变成0、变成1、……、变成k-1,但绝对不能保持k不变。不知道你能不能根据这个联想到Nim游戏,Nim游戏的规则就是:每次选择一堆数量为k的石子,可以把它变成0、变成1、……、变成k-1,但绝对不能保持k不变。这表明,如果将n枚棋子所在的顶点的SG值看作n堆相应数量的石子,那么这个Nim游戏的每个必胜策略都对应于原来这n枚棋子的必胜策略!

​ 所以我们可以定义有向图游戏的和(Sum of Graph Games):设G1、G2、……、Gn是n个有向图游戏,定义游戏G是G1、G2、……、Gn的和(Sum),游戏G的移动规则是:任选一个子游戏Gi并移动上面的棋子。Sprague-Grundy Theorem就是:g(G)=g(G1)^g(G2)^ . . . ​ ...​ ...^g(Gn)。也就是说,游戏的和的SG函数值是它的所有子游戏的SG函数值的异或。

复杂度:

​ 无

例题

​ hdu 1536 S-Nim

​ hdu 1729 Stone Game

​ hdu 1730 Northcott Game

​ hdu 2524 A Chess Game

Gym - 101128GGame of Cards

代码

解题模型:

1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。

 即sg(G)=sg(G1)\^sg(G2)\^...\^sg(Gn)。

2.分别考虑没一个子游戏,计算其SG值。

SG值的计算方法:(重点)

 1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);

​ 2.可选步数为任意步,SG(x) = x;

​ 3.可选步数为一系列不连续的数,用模板计算。

模板1:打表


//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;
            }
        }
    }
}

模板二: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;
}

3.计算sg(G)=sg(G1)^sg(G2)^…^sg(Gn),

sg(G)=0,即P-Position,即先手比败。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值