简单博弈论:公平组合游戏

若一个游戏满足:

  1. 由两名玩家交替行动
  2. 在游戏进行的任意时刻,可以执行的合法行动与轮到哪位玩家无关
  3. 不能行动的玩家判负

则称该游戏为一个公平组合游戏。

尼姆游戏(NIM)属于公平组合游戏,但常见的棋类游戏,比如围棋就不是公平组合游戏,因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和3。

有向图游戏

将公平组合游戏看作一个有向无环图,图中有一个唯一的起点,在起点放有一枚棋子。两名玩家交替把这枚棋子沿着有向边进行移动,每次可以移动一步,无法移动判负。任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面看成图中的一个节点,并且从每个局面向沿着合法行动能够到达的下一个局面连接有向边。

Mex运算

S S S表示一个非负整数集合,定义 m e x ( S ) mex(S) mex(S)为求出不属于集合 S S S的最小非负整数的运算即: m e x ( S ) = m i n ( x ) mex(S) = min(x) mex(S)=min(x) x x x属于自然数,且 x x x不属于集合 S S S
例如:集合$S = {1, 2, 3} $,那么 m e x ( S ) = 0 mex(S) = 0 mex(S)=0 0 0 0为不在集合 S S S中的最小自然数。

SG函数

在有向图游戏中,对于每个结点 x x x,设从 x x x出发共有 k k k条边,分别到达节点 y 1 , y 2 , . . . , y k y_1, y_2, ..., y_k y1,y2,...,yk,定义 S G ( x ) SG(x) SG(x) x x x的后继节点 y 1 , y 2 , . . . , y k y_1, y_2, ..., y_k y1,y2,...,yk S G SG SG函数值构成的集合S、再执行 m e s ( S ) mes(S) mes(S)运算的结果,即: S G ( x ) = m e x ( S G ( y 1 ) , S G ( y 2 ) , . . . , S G ( y k ) ) SG(x) = mex(SG(y_1), SG(y_2), ..., SG(y_k)) SG(x)=mex(SG(y1),SG(y2),...,SG(yk))
特别地,整个有向图游戏 G G G S G SG SG函数值被定义为有向图游戏起点 S S S S G SG SG函数值, S G ( G ) = S G ( S ) SG(G) = SG(S) SG(G)=SG(S)

例如,有如下有向图游戏,黄色为起点,绿色为终点,首先定义 S G ( 终 点 ) = 0 SG(终点) = 0 SG()=0,那么可以推导出其它各点的 S G ( ) SG() SG()函数值:

有向图游戏.png

在有向图游戏中判断某个状态 x x x(图中一点),如果:

  1. S G ( x ) = 0 SG(x) = 0 SG(x)=0,为必败态。首先终点状态是 0 0 0,任何一个 0 0 0状态,在有向图中都无法直接到达一个 0 0 0状态。先手如果为0,无法通过操作将状态变为0,即留给后手一个必胜态。
  2. S G ( x ) ≠ 0 SG(x) ≠ 0 SG(x)=0,为必胜态。任何一个非 0 0 0状态,在有向图中必然可以直接到达一个 0 0 0状态。先手如果非 0 0 0,可以通过操作将状态变为 0 0 0,即留给后手一个必败态。

有向图游戏的和

G 1 , G 2 , . . . , G m G_1, G_2, ... , G_m G1,G2,...,Gm m m m个有向图游戏。定义有向图游戏 G G G,它的行动规则是任选某个有向图游戏 G i G_i Gi,并在 G i G_i Gi上行动一步。 G G G被称为有向图游戏 G 1 , G 2 , . . . , G m G_1, G_2, ... , G_m G1,G2,...,Gm的和。
有向图游戏的和的 S G SG SG函数值等于它包含的各个字游戏 S G SG SG函数值的异或和,即:
S G ( G ) = S G ( G 1 ) ⊕ S G ( G 2 ) ⊕ . . . ⊕ S G ( G m ) SG(G) = SG(G1) \oplus SG(G2) \oplus ... \oplus SG(G_m) SG(G)=SG(G1)SG(G2)...SG(Gm)

证明:参考尼姆游戏的证明。

#include <bits/stdc++.h>

using namespace std;

const int N = 110, M = 10010;
int k, n;
//s[]表示每次拿取的石子数量的集合
//dp[]记忆化搜索,dp[i]记录sg[i]
int s[N], dp[M];

int sg(int x){
    //记忆化搜索,如果已经计算过,直接返回
    if(dp[x] != -1) return dp[x];
    //S存放从x出发、所有能到的局面的sg函数值
    set<int> S; 
    for(int i = 0; i < k; i++){
        int sum = s[i]; //取走个数
        if(x >= sum){ //能从x中能取走sum
            S.insert(sg(x - sum)); //将取走后的局面加入到集合S中
        }
    }
    //mex(S),求集合S中不存在的最小的自然数
    for(int i = 0; ; i++){
        if(S.count(i) == 0) return dp[x] = i;
    }
    
}

int main(){
    
    memset(dp, -1, sizeof dp);
    
    cin>>k;
    for(int i = 0; i < k; i++) cin>>s[i];
    cin>>n;
    int res = 0;
    for(int i = 0; i < n; i++){
        int x;
        cin>>x;
        res ^= sg(x);
    }
    if(res) puts("Yes");
    else puts("No");
    
    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

少儿编程乔老师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值