博弈论之SG函数——杨子曰数学

博弈论之SG函数——杨子曰数学

超链接:数学合集


我终于略懂了的SG函数的皮毛!


首先,我们要知道一个运算——mex(S)

表示的就是不属于集合S的最小的自然数

比如: m e x ( { 0 , 1 , 4 , 5 } ) = 2 mex(\{0,1,4,5\})=2 mex({0,1,4,5})=2 m e x ( { 2 , 3 , 5 } ) = 0 mex(\{2,3,5\})=0 mex({2,3,5})=0之类的


然后你还要知道几个博弈论中应该知道的东西:

  • 一个状态的后继状态,指的是通过一个操作可以从当前状态达到的状态
  • 必败态:就是当前轮到你,你无论怎么玩都是输,则当前的状态是必败态
    那怎么判断一个状态是不是必败态捏?
    就是它所有的后继状态都是必胜态(猜也能猜出这是啥),那么这个状态就是必败态
  • 必胜态:就是轮到你,你能机智地做一些操作,使得你必赢,则当前这个状态就是必胜态
    那怎么判断一个状态是不是必胜态捏?
    就是这个状态的后继状态中有一个必败态,那么这个状态就是必胜态(你只需要把它弄成那个必败态就好了)

这也就是说明,一个状态不是必败态,就是必胜态(就看后继状态里有没有必败态)


然后,我们今天的主角就登场了——SG函数

你现在可能一脸懵逼(・∀・(・∀・(・∀・*),SG函数,什么鬼?

不要紧张,你现在可以这样理解:对于每一个游戏局面都有一个函数来描述它的状态,我们就管这个函数叫做SG函数

(但是你还是要注意,不同的状态有可能有相同的SG值,往下读,你就可以更加深刻地理解它了)

首先,告诉大家的是这样两个事情:

  • SG值大于0,这是一个必胜态;SG值等于0,这是一个必败态
  • y 1 , y 2 , ⋯   , y k y_1,y_2, \cdots,y_k y1,y2,,yk表示状态s的后继状态,那么 S G ( s ) = m e x ( S G ( y 1 ) , S G ( y 2 ) , ⋯   , S G ( y k ) ) SG(s)=mex({SG(y_1),SG(y_2),\cdots,SG(y_k)}) SG(s)=mex(SG(y1),SG(y2),,SG(yk))

先不要管下面这个式子的其他疑惑(我知道你对这个式子有很多疑惑),我们就先来看一看为什么下面这个式子能满足上面那个结论

首先,如果一个状态的后继状态全是必胜态,那么所有的 S G ( y 1 ) , S G ( y 2 ) , ⋯   , S G ( y k ) SG(y_1),SG(y_2),\cdots,SG(y_k) SG(y1),SG(y2),,SG(yk)都是大于0的,然后对它们取mex,得到的数一定是0,也就是必败态,满足条件!

如果,一个状态的后继状态里,有必败态,也就是在 S G ( y 1 ) , S G ( y 2 ) , ⋯   , S G ( y k ) SG(y_1),SG(y_2),\cdots,SG(y_k) SG(y1),SG(y2),,SG(yk)中有0这个数字,那么我们在取完mex以后得到的数一定不是0,也就是必胜态,同样满足条件!

完美!

那么我们为什么要用这样的式子来计算SG呢?

我们来深入理解一下:
如果一个状态s的SG值等于a,那么根据:
S G ( s ) = m e x ( S G ( y 1 ) , S G ( y 2 ) , ⋯   , S G ( y k ) ) SG(s)=mex({SG(y_1),SG(y_2),\cdots,SG(y_k)}) SG(s)=mex(SG(y1),SG(y2),,SG(yk))

我们可以得到的是:在集合 { S G ( y 1 ) , S G ( y 2 ) , ⋯   , S G ( y k ) } \{SG(y_1),SG(y_2),\cdots,SG(y_k)\} {SG(y1),SG(y2),,SG(yk)}中一定有0~a-1中的每个数字

这就说明的一个事情:一个SG值等于a的状态保证能转移到SG值为0~a-1的一个状态

到这里我们就可以用它解决一些简单的博弈论题目了:

现在有5颗石子,两个人轮流抓,每轮可以抓走1颗,3颗,或者4颗,抓完的人获胜,问:先手必胜,还是必败?

这道题就是问我们SG(5)的值

我们先来看一下边界条件:场上剩0颗,显然是必败态,也就是SG(0)=0,然后我们就可以开始递推了:
S G ( 1 ) = m e x ( S G ( 0 ) ) = 1 SG(1)=mex(SG(0))=1 SG(1)=mex(SG(0))=1

S G ( 2 ) = m e x ( S G ( 1 ) ) = 0 SG(2)=mex(SG(1))=0 SG(2)=mex(SG(1))=0

S G ( 3 ) = m e x ( S G ( 0 ) , S G ( 2 ) ) = 1 SG(3)=mex(SG(0),SG(2))=1 SG(3)=mex(SG(0),SG(2))=1

S G ( 4 ) = m e x ( S G ( 0 ) , S G ( 1 ) , S G ( 3 ) ) = 2 SG(4)=mex(SG(0),SG(1),SG(3))=2 SG(4)=mex(SG(0),SG(1),SG(3))=2

S G ( 5 ) = m e x ( S G ( 1 ) , S G ( 2 ) , S G ( 4 ) ) = 3 SG(5)=mex(SG(1),SG(2),SG(4))=3 SG(5)=mex(SG(1),SG(2),SG(4))=3

So,先手必胜


SG函数的应用怎么可能只有这么一点呢?

我们的题目往往不会这么单一,我们的状态是可以由多种小状态组成的


比如这样一道题目(很多博客讲到这里都喜欢用经典Nim游戏举例子,但我觉得下面的例子更加清晰):

给定一张N×M的矩形网格纸,两名玩家轮流行动。在每一次行动中,可以任选一张矩形网格纸,沿着某一行或者某一列的格线将之剪成两部分。首先剪出1×1的玩家获胜。两名玩家都采用最优策略,问先手是否必胜?

Input Data
The input contains multiple test cases. Each test case contains only two integers W and H (2 <= W, H <= 200) in one line, which are the width and height of the original paper.

Output Data
For each test case, only one line should be printed. If the one who cut first can win the game, print “WIN”, otherwise, print “LOSE”.

Input Sample

2 2
3 2
4 2

Output Sample

LOSE
LOSE
WIN

这道题你就会发现一个状态是可以由多种小状态组成的

当前是一个n*m的矩阵,这是我们的母状态,我们可以通过枚举从哪里切开(其实就是在枚举后继状态),可以得到两个矩阵,这两个矩阵有分别对应两个子状态,那么我们能不能根据子状态的SG值算出母状态是SG值捏?

当然可以,我们甚至可以得到这样的式子:

如果x是母状态, y 1 , y 2 , ⋯   , , y k y_1,y_2,\cdots,,y_k y1,y2,,,yk是x的子状态,有:
S G ( x ) = S G ( y 1 ) ⊕ S G ( y 2 ) ⊕ ⋯ ⊕ S G ( y k ) SG(x)=SG(y_1)\oplus SG(y_2)\oplus \cdots\oplus SG(y_k) SG(x)=SG(y1)SG(y2)SG(yk)

神不神奇!

我们来简单地证明一下:

要证明上面那个事情,就是要证明这样一个东东(注意,A+B必须被分成A和B):
S G ( A + B ) = S G ( A ) ⊕ S G ( B ) SG(A+B)=SG(A)\oplus SG(B) SG(A+B)=SG(A)SG(B)

我们用一个非常佛系的方法来证明这个事情:

假设这件事情是对的……

首先,由于A+B必须被分成A和B,那么A+B之后可以达到的状态,就是A可以达到的状态 ⋃ \bigcup B可以达到的状态

然后,假设SG(A)=1,SG(B)=2,(前面已经说了,当前状态可以转移到SG值小于它的SG值的状态)那么可能达到的状态是SG值是 0 ⊕ 2 = 2 、 1 ⊕ 0 = 1 、 1 ⊕ 1 = 0 0 \oplus 2 = 2、1 \oplus 0 = 1、1 \oplus 1 = 0 02=210=111=0
然后你有没有发现,小于 1 ⊕ 2 = 3 1 \oplus 2 = 3 12=3的值都可以表示出来

假设SG(A)=1,SG(B)=3,那么可能达到的状态是SG值是那么可能达到的状态是SG值是 0 ⊕ 3 = 3 、 1 ⊕ 0 = 1 、 1 ⊕ 1 = 0 、 1 ⊕ 2 = 3 0 \oplus 3 = 3、1 \oplus 0 = 1、1 \oplus 1 = 0、1 \oplus 2 = 3 03=310=111=012=3
然后你有没有发现,小于 1 ⊕ 3 = 1 1 \oplus 3 = 1 13=1的值都可以表示出来

假设SG(A)=2,SG(B)=3,那么可能达到的状态是SG值是那么可能达到的状态是SG值是 0 ⊕ 3 = 3 、 1 ⊕ 3 = 2 、 2 ⊕ 0 = 2 、 2 ⊕ 1 = 3 、 2 ⊕ 2 = 0 0 \oplus 3 = 3、1 \oplus 3 = 2、2 \oplus 0 = 2、2 \oplus 1 = 3、2 \oplus 2 = 0 03=313=220=221=322=0
然后你有没有发现,小于 2 ⊕ 3 = 1 2 \oplus 3 = 1 23=1的值都可以表示出来

机智的你一定发现了什么惊悚的规律!

A和B这两个状态可以表示出所有SG值小于 S G ( A ) ⊕ S G ( B ) SG(A)\oplus SG(B) SG(A)SG(B)的至少一个状态,也就是说 S G ( A ) ⊕ S G ( B ) SG(A)\oplus SG(B) SG(A)SG(B)就是A状态和B状态第一个达不到的状态的SG值

这件事情证明对于我来说有些困难,只能找找规律了,想要知道怎么证明可以戳这里

然后你有没有发现,SG值小于 S G ( A ) ⊕ S G ( B ) SG(A)\oplus SG(B) SG(A)SG(B)的状态都是可以被我们达到的,完全符合我们SG(A+B)的定义有没有!

就这样,非常不严谨地得证了!


我们在回到上面的那题,假设我们现在拿到手的矩阵是n*m的,我们枚举他从哪里切开,也就是在枚举它的后继状态,对于每个后继状态是由两个矩阵组成的,我们可以利用上面的公式求出这个后继状态的SG值,在对所有后继状态的SG值取个mex就可以得到当前矩阵的SG值了!

那么临界情况是什么捏?

显然如果出现了 1 ∗ m 1*m 1m或者 n ∗ 1 n*1 n1这样的矩阵的话,只要剪出一个 1 ∗ 1 1*1 11,就必胜了

然而要到这两个情况必定途经: 2 ∗ 3 , 3 ∗ 2 , 2 ∗ 2 2*3,3*2,2*2 23,32,22,只有它们的后继状态全是形如上面那两个矩阵的,So,它们是必败态

而题目中的长和宽都是保证大于等于2的,So,边界情况我们就记录:SG[2][3]=SG[3][2]=SG[2][2]=0

OK,完事


c++代码(POJ2311):

#include<bits/stdc++.h>
using namespace std;

const int maxn=205;

int sg[maxn][maxn];

int getsg(int n,int m){
    if (sg[n][m]!=-1) return sg[n][m];
    bool flag[maxn*2];
    memset(flag,0,sizeof(flag));
    for (int i=2;i<=n-i;i++){
        flag[getsg(i,m)^getsg(n-i,m)]=1;
    }
    for (int i=2;i<=m-i;i++){
        flag[getsg(n,i)^getsg(n,m-i)]=1;
    }
    for (int i=0;i<=300;i++){
        if (flag[i]==0){
            return sg[n][m]=i;
        }
    }
}

int main(){
    memset(sg,-1,sizeof(sg));
    sg[2][2]=sg[2][3]=sg[3][2]=0;
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        if (getsg(n,m)) puts("WIN");
        else puts("LOSE");
    }
    return 0;
}

参考:
https://zhuanlan.zhihu.com/p/20611132
《算法竞赛进阶指南》 李煜东 著

于HG机房

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值