【甜椒&ACM】博弈论入门(Nim、sg定理)

博弈论入门(Nim、sg定理)

通常由以下形式构成:给定一个游戏规则,甲、乙轮流出手行动,最后无法行动的人失败,问获胜方是谁/是否存在先手必胜策略等等。

游戏的前提是,默认“双方都最聪明”,对于每一步,两方都能给出最优策略。因此,游戏的情形,可以说是一种“胜负手”的转移。若先手状态 f ( n ) f(n) f(n),在先手操作后, f ( n ) f(n) f(n)可以变成 f ( m ) f(m) f(m),且 f ( m ) f(m) f(m)是必败态,则 f ( n ) f(n) f(n)为必胜态。因此,双方的目的就是不断将局面转移到“必败态”,使得对手下一步无法操作。

一、热身

Roy&October之取石子

共有 n n n 个石子,两人每次都只能取 p k p^k pk 个( p p p 为质数, k k k 为自然数,且 p k p^k pk 小于等于当前剩余石子数),谁取走最后一个石子,谁就赢了。

结论:当且仅当 n n n 为6的倍数时,先手必败。

这是一道难度较低的入门博弈题。一个常用技巧是对于小数据进行枚举:我们可以发现, n = 1 ( 即 p 0 ) , 2 , 3 , 4 ( 即 2 2 ) , 5 n=1(即p^0),2,3,4(即2^2),5 n=1(p0),2,3,4(22),5 时,先手都可以一次性取完石子,先手必胜。而 n = 6 n=6 n=6 时,由于 6 = 2 × 3 6=2×3 6=2×3 ,不存在 p k p^k pk 满足要求,故先手必败。

因此我们可以推演出:对于任意 6 x = 2 × 3 × x 6x=2×3×x 6x=2×3×x,不存在满足条件的 p k p^k pk可以一次取完,因此,取走一次后必然形成 6 x ′ + k , k ∈ [ 1 , 5 ] 6x'+k,k∈[1,5] 6x+kk[1,5] 的情况,则后手可以拿走 k k k 个,不断把不利的局面转移给先手,最终拿走 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5 中的一个数,获胜并结束比赛。

模板题:https://www.luogu.com.cn/problem/P4018

变形

共有 n n n 个石子,两人每次都只能取 p k p^k pk个( p p p 为质数,k=0或1,且 p k p^k pk小于等于当前剩余石子数),谁取走最后一个石子,谁就赢了。

结论:当且仅当 n n n 为4的倍数时,先手必败。

有了前一题的基础,我们继续枚举:当 n = 1 , 2 , 3 , 5 , 7 n=1,2,3,5,7 n=1,2,3,5,7 时,直接取完,先手必胜; n = 4 n=4 n=4 时,脑补排列可得先手必败; n = 6 n=6 n=6 时,可以取走两个转移到必败态$ n=4$ ,先手必胜; n = 8 n=8 n=8 时,可能到达的状态仅有 n = 4 n=4 n=4 为必败点,需要取走4个,为非法移动,故先手必败。

对于一个非质奇数,我们可以付出不多于3的代价将它转移为 4 x 4x 4x状态,故所有奇数都是必胜态。那么,只需要证明,对于一个偶数 n n n ,只有 n = 4 x n=4x n=4x 时必败。显然, n = 4 x + 2 n=4x+2 n=4x+2 时只需代价2就可以完成转移,而 4 x + 4 4x+4 4x+4不可能转移到 4 x 4x 4x,所有 4 x ′ 4x' 4x 都是必败态。

链接:https://www.luogu.com.cn/problem/P4860

二、Nim游戏

n n n堆石子,每堆有 a i a_i ai个。两个玩家必须轮流取走任意一堆的任意个(不含0)物品,取走最后一个物品的人获胜。

结论:当且仅当 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = 0 a[1]a[2]a[3]...a[n]=0 时,先手必败。

证明:

我们需要证明——

1、没有后继状态的状态是必败状态。

2、 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] ≠ 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] ≠ 0 a[1]a[2]a[3]...a[n]=0时,存在一种取法,使得 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = 0 a[1]a[2]a[3]...a[n]=0

3、 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = 0 a[1]a[2]a[3]...a[n]=0时,不存在某种移动使得 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = 0 a[1]a[2]a[3]...a[n]=0

当以上三条得证时,随着玩家操作,异或和必然不断在0和非0之间转换,则初始为0时,经过偶数次变换,还是为0,先手必败。

证明1:没有后继的状态,即 a [ 1 ] = a [ 2 ] = a [ 3 ] = . . . = a [ n ] = 0 a[1]=a[2]=a[3]=...=a[n] = 0 a[1]=a[2]=a[3]=...=a[n]=0。此时,满足 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = 0 a[1]a[2]a[3]...a[n]=0

证明2:设 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = k a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = k a[1]a[2]a[3]...a[n]=k,将它们的二进制计算以竖式排列,由异或的定义, k k k的二进制最高位 i d x idx idx 上必然有奇数个 1 1 1。取出一个满足 i d x idx idx 位上为 1 1 1 a [ i ] a[i] a[i],将 a [ i ] a[i] a[i]改变成 t = a [ i ] ⊕ k t=a[i]⊕k t=a[i]k

∵ a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ i ] ⊕ . . . ⊕ a [ n ] = k ∵a[1] ⊕ a[2] ⊕ a[3] ⊕ ...⊕a[i]⊕... ⊕ a[n] = k a[1]a[2]a[3]...a[i]...a[n]=k

∴ a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ ( a [ i ] ⊕ k ) ⊕ . . . ⊕ a [ n ] = k ⊕ k = 0 ∴a[1] ⊕ a[2] ⊕ a[3] ⊕ ...⊕(a[i]⊕k)⊕... ⊕ a[n] = k⊕k=0 a[1]a[2]a[3]...(a[i]k)...a[n]=kk=0

∵ a [ i ] ∵a[i] a[i] k k k最高位相同,∴ t < a [ i ] ⊕ k t<a[i]⊕k t<a[i]k

即:从第 i i i堆石子中拿走a[i]-(a[i]⊕k)个石子,即可将该状态转换为满足 a [ 1 ] ⊕ a [ 2 ] ⊕ a [ 3 ] ⊕ . . . ⊕ a [ n ] = 0 a[1] ⊕ a[2] ⊕ a[3] ⊕ ... ⊕ a[n] = 0 a[1]a[2]a[3]...a[n]=0 的状态。

证明3:对于任意 x x x x ⊕ 0 = x x⊕0=x x0=x,因此对左式异或任意不为0的值去改变一堆石子数,右边必然不为0。

模板题:https://www.luogu.com.cn/problem/P1247

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int n, a[N];
int main() {
    cin >> n;
    int tot = 0;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        tot ^= a[i];
    }
    if (!tot) cout << "lose";
    else {
        for (int i = 0; i < n; ++i) {
            if ((a[i] ^ tot) < a[i]) {
                cout << a[i] - (a[i] ^ tot) << ' ' << i + 1 << '\n';
                a[i] ^= tot;
                break;
            }
        }
        for (int i = 0; i < n; ++i) {
            cout << a[i] << ' ';
        }
    }
    return 0;
}

三、由Nim到SG定理

首先,介绍一下前文提到的“先手必胜"和”先手必败“(即后手必胜)的官方名——

N-状态(next player):先手必胜,对于该点,下一个行动的玩家将会获胜。所谓“必胜点”。

P-状态(previous player):后手必胜,对于该点,前一个行动的玩家将会获胜。所谓“必败点”。

对于N-状态和P-状态,有如下性质:

  • 所有终结点是P-状态
  • 一个点为P-状态,当且仅当它的所有后继都为N-状态;
  • 一个点为N-状态,当且仅当它的后继存在一个P-状态;

我们将博弈过程抽象为一张有向无环图,即从某一顶点出发,不断转移到下一状态,直到无路可走。

在这里插入图片描述

(一种N-P-状态转移示意)

理解到这里以后,引入 m e x 函 数 mex函数 mex S G 函 数 SG函数 SG 的概念:

定义 m e x 函 数 mex函数 mex的值为不属于集合 S S S中的最小非负整数,

mex(S) = min{x} (x∉S,x∈N)

例: m e x { 0 , 2 , 3 } = 1 mex\{0,2,3\}=1 mex{0,2,3}=1 m e x { 1 , 2 , 3 , 6 , 7 } = 4 mex\{1,2,3,6,7\}=4 mex{1,2,3,6,7}=4 m e x { } = 0 mex\{\}=0 mex{}=0

定义 S G ( x ) SG(x) SG(x) 表示 x x x 后继状态 x 1 、 x 2 、 . . . 、 x n x_1、x_2、...、x_n x1x2...xn ( x > x i ) (x>x_i) (x>xi) S G SG SG 函数集合的 m e x mex mex值。

SG(x) = mex{SG(x1),SG(x2),…,SG(xn)}

SG[0] = 0

S G ( x ) = 0 SG(x)=0 SG(x)=0 时,即它已经无路可走,则此时的 x x x为P-状态。

借用参考链接第二位的博主@Enstein_Jun 的一个实例:

取石子问题

有1堆n个的石子,每次只能取{ 1, 3, 4 }个石子,先取完石子者胜利,那么各个数的SG值为多少?

SG[0]=0,f[]={1,3,4} (f表示可用改变量的状态集)

x=1 时,可以取走1 - f{1}个石子,剩余{0}个,所以 SG[1] = mex{ SG[0] }= mex{0} = 1;

x=2 时,可以取走2 - f{1}个石子,剩余{1}个,所以 SG[2] = mex{ SG[1] }= mex{1} = 0;

x=3 时,可以取走3 - f{1,3}个石子,剩余{2,0}个,所以 SG[3] = mex{SG[2],SG[0]} = mex{0,0} =1;

x=4 时,可以取走4- f{1,3,4}个石子,剩余{3,1,0}个,所以 SG[4] = mex{SG[3],SG[1],SG[0]} = mex{1,1,0} = 2;

x=5 时,可以取走5 - f{1,3,4}个石子,剩余{4,2,1}个,所以SG[5] = mex{SG[4],SG[2],SG[1]} =mex{2,0,1} = 3;

以此类推…

x 0 1 2 3 4 5 6 7 8…

SG[x] 0 1 0 1 2 3 2 0 1…

由上述实例我们就可以得到SG函数值求解步骤,那么计算1~n的SG函数值步骤如下:

1、使用 数组f 将 可改变当前状态 的方式记录下来。

2、然后我们使用 另一个数组 将当前状态x 的后继状态标记。

3、最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。

4、我们不断的重复 2 - 3 的步骤,就完成了 计算1~n 的函数值。

sg函数代码:

int f[N],SG[MAXN],vis[MAXN];
void  getSG(int n){
    memset(SG,0,sizeof(SG));
    //SG[0]=0
    for(int i = 1; i <= n; i++){
        memset(vis,0,sizeof(vis));
        for(int j = 0; f[j] <= i && j <= N; j++)
            vis[SG[i-f[j]]] = 1;  //将i能到达的后继状态设为可到达
        for(int j = 0;; j++) {
            if(!vis[j]){   //求出当前后继状态集合中的mex,最小未被访问过的数
            	SG[i] = j;
            	break;
        	}
        }
    }
}

结合上述,引出 S G 定 理 SG定理 SG

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

即:sg(X) = sg(x[1]) ^ sg(x[2]) ^ … ^ sg(x[n])

这时,再看nim博弈,实际上异或和为0时,就是sg(X)=0的情况。和上述对nim异或和为0为什么是P-状态的证明思路大致相同。因此,在很多情况下,求出sg函数并且分析sg(X)=0的情况,就是博弈题的突破口。

四、相关名词

对称博弈

阶梯波伊

反Nim博弈

Moore’s Nimk

树上博弈

图上博弈

巴士博弈

威佐夫博弈
(待补充)

参考资料:

http://keyblog.cn/article-47.html

https://blog.csdn.net/luomingjun12315/article/details/45555495

https://blog.csdn.net/bestsort/article/details/88197959

http://oi-wiki.com/math/game-theory/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值