zoukankan      html  css  js  c++  java
  • 学习笔记 SG函数

    SG函数

    Nim游戏

    n堆石子 (a_1 a_2 a_3 a_4 ...... a_n)

    规则(......)

    (a_1 xor a_2 xor a_3 xor a_4 xor ...... xor a_n==0) ? 后手胜 : 先手胜

    太简单了? ? ? hhh

    上升一下规则

    1.每一次可以从第1堆石子取出1或者2或者3颗

    2.每一次可以从第2堆石子取出奇数颗

    3.每一次可以从第3堆以及后面取出任意颗

    求必胜策略

    (一脸懵逼......)


    不多说了 正式开始

    首先我们定义一个DAG

    一个入度为零的点作为起点

    起点上有一个棋子 先后手沿有向边轮流移动 无法移动者lose

    这就基本上把博弈论转换为了抽象模型 ---> 图论 ? ? ?

    SG就是在这个DAG上面定义然后&^$@!&#%......


    (1)(mex)运算

    这是一个基于集合的运算

    表示 最小 的不属于该集合的 非负整数

    例如 (mex{0,1,3,5,6,8,9}=2)


    (2)定义

    当前SG函数如下 : (SG(x)=mex{SG(y)|y是x的后继})

    ......只有定义是没有用的

    ①所有出度为零的点x(我们简称为汇点) (SG(x)=0)

    (SG(x)=0) 该节点是必败点


    必败态:

    对于当前状态 TA的后继状态 全部 都是必胜态 那么TA就是必败态

    当前状态 无法移动也就是没有后继状态 那么TA就是必败态


    如果当前点是一个汇点的话 根据定义 必败无疑

    但是如果不是呢 ? ? ?

    根据(mex)的定义 该节点的后继节点当中所有后继节点 (SG!=0)

    (SG(x)!=0) 该节点对应状态是必胜状态

    那么后继节点中一定存在(SG=0)的节点

    又回到上面那个(SG(x)=0)的问题了

    这样会一直递归到x是一个汇点

    (SG(x)=0) 那么(TA)的前驱节点(SG!=0) 且是必胜态 那么应该就可以理解了


    必胜态:

    对于当前状态 TA的后继状态 至少有一个 是必败态 那么TA就是必胜态

    必败态:

    (1)对于当前状态 TA的后继状态 全部 都是必胜态 那么TA就是必败态

    (2)当前状态 无法移动也就是没有后继状态 那么TA就是必败态


    (SG(x)=0) 对应TA是汇点 或者后继结点(min{SG(y)|x can go to y}>0)

    恰好对应必败态定义

    (SG(x)!=0) 对应TA的后继结点 (0∈{SG(y)|x can go to y})

    恰好对应必胜态定义

    (3)实战

    说了定义还是一脸蒙蔽 所以 手玩一下? ? ?

    ①普通Nim游戏(请使用SG()求解)

    n堆石子 (a_1 a_2 a_3 a_4 ...... a_n)

    对于任意一堆石子(a_i(1<=i<=n))

    我们可以取([1,a_i])颗 那么剩余石子的范围就是([0,a-i-1])

    (SG(i)=a_i)

    等一下 我们相当于是把每一个石子堆看成了一个游戏

    但是怎么合并呢? ? ?

    直接上定理:(SG_{tot}=SG(1) xor SG(2) xor SG(3) xor SG(4) xor ...... xor SG(n))

    也就是游戏的SG值就是子游戏的SG值得异或和

    然后(SG_{tot}=0) 对应必败态 恰好石子堆的异或和为0 就是后手必胜

    是不是有些眉目了 ? ? ?

    SG定理的证明:【https://www.zhihu.com/question/51290443/answer/125105697】

    因为我太菜鸡所以当然不会证明辣

    ②文艺Nim游戏(请使用SG()求解)

    每一次可以从第1堆石子取出1或者2或者3颗

    每一次可以从第2堆石子取出奇数颗

    每一次可以从第3堆以及后面取出任意颗

    其余不变

    用SG求解

    先埋一个局。。。。。。

    SG模板

    normal

    void calc_SG(int x)
    {
        for(i ...枚举所有后继状态)
        vis[SG[i]]=1;
    }
    
    memset(vis,0,sizeof vis);
    calc_SG(x);
    for(int i=0;;++i) if(!vis[i]) {SG[x]=i;break;}
    

    时间戳优化 (卡常小技巧)

    //memset(vis,0,sizeof vis) 会被卡常 所以我们尝试优化
    
    void calc_SG(int x)
    {
        for(i ...枚举所有后继状态)
        vis[SG[i]]=tot;
    }
    
    ++tot;
    calc_SG(x);
    for(int i=0;;++i) if(vis[i]!=tot) {SG[x]=i;break;}
    

    例题

    【HDU1847】

    【HDU1848】

    我来解上面的局

    对于第一种情况 这就是巴什博奕 (SG[x]=mex{SG[x-3],SG[x-2],SG[x-1]})

    参考博弈树打表也是可以理解的 通式就是(SG[x]=x%4)

    那么对于第二种情况(SG[x]=x%2)

    也就是(SG[x]=mex{SG[x-1],SG[x-3],......,SG[(x+1)\%2]})

    根据博弈树打表也可以理解

    第三种情况就是(Nim)游戏 可以选择一个一个(SG)异或 也可以直接计算(SG3)

    最终的(SG_{tot}=SG_1 igoplus SG_2 igoplus SG_3)

    具体的见例题吧 cdy:别诶

  • 相关阅读:
    LeetCode 链表题总结
    分布式系统 MIT 6.824 Lab 1: MapReduce 准备
    Consul 入门(二)
    Consul 入门
    IDEA批量修改变量快捷键
    Spring源码解析-JdbcTemplate
    SpringMVC源码解析-HTTP请求处理和分发
    SpringMVC源码解析-DispatcherServlet启动流程和初始化
    发现一个网站可以看英文版的harry potter小说,好东西分享一下哈
    TypeSrcript如何引入第三方库 如果加d.ts以及async await如何使用 demo,只有代码,文字后续补充
  • 原文地址:https://www.cnblogs.com/LovToLZX/p/13757932.html
Copyright © 2011-2022 走看看