• 博弈——sg函数


    Nim游戏: 

       1. 一个状态是必败状态当且仅当它的所有后继都是必胜状态。

     2. 一个状态是必胜状态当且仅当它至少有一个后继是必败状态。

    对于Nim游戏来说,早有科学家给出了一个定理(Bouton定理):状态(x1,x2.....xn)为必败状态当且仅当x1^x2^......^xn= 0,即把所有数进行异或和操作,也称Nim sum。【能够证明,当Nim sum为0 时为必败状态,非0 时为必胜状态,这是因为,当前状态为0 时,进行的某个操作总能使Nim sum变为非0(因为改变任何一个数字(即任何一堆石子),它的二进制形式会有一位或多位的变化,此时原本各位上都为0 的Nim sum就会有所变动(某些位会变成1)),即所有后继都是必胜状态了;当前状态为非0 时,必定会有某个操作能使Nim sum变为0(只需在Nim sum二进制上那些为1的位上面着手即可),即一定有一个后继状态为必败态。这就是Bouton定理。】

    Bouton定理实质上是SG定理的具体应用。什么是SG定理呢?

    SG定理:

    Sprague-Grundy定理(SG定理):游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。而Bouton定理就是Sprague-Grundy定理在Nim游戏中的直接应用,因为单堆的Nim游戏 SG函数满足 SG(x) = x。

    对于SG定理:

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

    这一步应该是非常简单的,就是定义了新的运算为mex。

    对于任意状态 x , 定义 SG(x) = mex(S),其中S是 x 后继状态的SG函数值的集合(就是上述mex中的数值)。最后返回值(也就是SG(X))为0为必败点,不为零必胜点。

    进一步解释一下S,就是题意中给出的可以移动的次数。举个例子来说,一堆石子,每次只能拿1,3,5,7个,那么S数组就是1,3,5,7。

    假如说是在一个游戏中有多个石子堆该怎么办了。我们只需要把对每个石子堆进行sg函数的调用,将得到的所有的值进行异或。得出来的结果为0则该情形为必败态。否则为必胜态。

    #include<cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    const int MAX_N = 100;
    int sg[MAX_N];//sg函数
    bool vis[MAX_N];//标记数组
    
    void solve(int n) {
        memset(vis, false, sizeof(vis));
        for (int i = 0; i < n; ++i)
                vis[sg[i]] = true;
        for (int i = 1; i <= n; ++i)//因为可以分成两堆,如果三堆,就写三重循环
            for (int j = 1; j <= n; ++j) {
                if (i + j == n) vis[sg[i] ^ sg[j]] = true;
            }
        int i;
        for (i = 0; ; ++i)//没有i < n,如果都不成立,最后i = n
            if (!vis[i]) break;
        sg[n] = i;
        cout << "sg[" << n << "]=" << i << endl;
    }
  • 相关阅读:
    数据字典/动态性能视图
    参数管理
    expdp实现oracle远程服务器导出到本地
    jquery 操作单选按钮
    vs2012加载T4MVC模板
    Asp.net Mvc 过滤器执行顺序
    oracle版本及字符集查询
    ora-01658: 无法为表空间*****中的段创建 INITIAL 区
    SmtpClient发送邮件
    盒模型padding和margin对滚动条位置的影响
  • 原文地址:https://www.cnblogs.com/qie-wei/p/12094122.html
Copyright © 2020-2023  润新知