Sprague-Grundy (SG) 函数及其应用

在算法竞赛中,博弈论问题通常涉及到两名玩家轮流操作。要理解复杂的博弈模型,通常从最基础、最经典的 Nim 游戏 开始。

Nim 游戏

Nim 游戏是博弈论中最基础的模型。

游戏规则

\(n\) 堆石子,第 \(i\) 堆石子数为 \(a_i\)。两名玩家轮流从任意一堆中取走任意数量的石子(至少取一个,至多取完该堆),最后取光石子的人获胜。

Nim 和

Nim 游戏的胜负由所有堆石子数的异或和(称为 Nim 和)决定:\(S = a_1 \oplus a_2 \oplus \cdots \oplus a_n\)

  • \(S \ne 0\),当前状态为必胜态,先手总能通过一次操作将 \(S\) 变为 0。
  • \(S = 0\),当前状态为必败态。无论先手如何操作,产生的 \(S'\) 必定不为 0。

证明

  1. 终局状态:当所有 \(a_i = 0\) 时,游戏结束,此时 \(S = 0 \oplus 0 \oplus \cdots \oplus 0 = 0\),这符合必败态的定义。
  2. S \neq 0 \Rightarrow S = 0$ 的转移:设 \(S\) 的二进制表示中最高位的 1 在第 \(k\) 位,那么至少存在一堆石子 \(a_i\),其第 \(k\) 位也是 1。显然 \(a_i \oplus S \lt a_i\),因为异或 \(S\) 之后,\(a_i\) 的第 \(k\) 位变成了 0,而更高的位没变。将第 \(i\) 堆石子数从 \(a_i\) 减少到 \(a_i' = a_i \oplus S\),新的异或和为 \(S' = S \oplus a_i \oplus (a_i \oplus S) = S \oplus S = 0\)
  3. \(S = 0 \Rightarrow S \neq 0\) 的转移:若从第 \(i\) 堆取走一些石子,使其从 \(a_i\) 变为 \(a_i' \ (a_i' \lt a_i)\)。新的异或和 \(S' = S \oplus a_i \oplus a_i' = 0 \oplus a_i \oplus a_i' = a_i \oplus a_i'\),由于 \(a_i \ne a_i'\),故 \(S' \ne 0\)

公平组合游戏

Nim 游戏属于一类被称为 公平组合游戏(Impartial Combinatorial Games) 的博弈,其特征如下:

  1. 有两名玩家,轮流进行操作。
  2. 在游戏的任意时刻,合法操作集合仅取决于当前的状态,而与当前轮到哪位玩家无关。
  3. 游戏在有限步内结束,且不存在平局。
  4. 遵循常规操作原则:最后一名进行合法操作的玩家获胜(即无法移动者输)。

mex 函数与 SG 函数

mex 函数(Minimum Excluded value)

\(\text{mex}(S)\) 定义为集合 \(S\) 中未出现的最小非负整数,如 \(\text{mex}(\{0, 1, 2, 4\}) = 3, \ \text{mex}(\{1, 3, 5\}) = 0\)

SG 函数

对于一个状态 \(u\),设其后继状态集合为 \(V = \{v_1, v_2, \dots, v_k\}\),则状态 \(u\) 的 SG 值定义为 \(\text{SG}(u) = \text{mex}(\{\text{SG}(v_1), \text{SG}(v_2), \dots, \text{SG}(v_k)\})\)

\(\text{SG}(u) = 0\) 表示该状态为必败态\(\text{SG}(u) \ne 0\) 表示该状态为必胜态

Sprague-Grundy 定理

SG 定理是博弈论中最核心的定理,它指出任何一个 ICG 都可以等价于一个 Nim 游戏中的一堆石子,其石子数为该状态的 SG 值

对于由 \(n\) 个独立的子游戏组成的复合游戏 \(G\),其总状态的 SG 值为各子游戏状态 SG 值的异或和:\(\text{SG}(G) = \text{SG}(G_1) \oplus \text{SG}(G_2) \oplus \cdots \oplus \text{SG}(G_n)\)

例题

例题:P10501 Cutting Game

给定一个 \(W \times H \ (2 \le W,H \le 200)\) 的矩形纸张,两名玩家轮流对纸张进行横向或纵向切割,每次切割将一片矩形纸张分成两片新的矩形。如果一名玩家切出了一个 \(1 \times 1\) 的格子,该玩家获胜。问在双方都采取最优决策略的情况下,先手是否必胜。

本题的关键在于获胜条件:切出 \(1 \times 1\) 格子的玩家获胜。观察发现,如果一个玩家在某一步切出了一个维度为 1 的矩形(例如 \(1 \times k\)\(k \times 1\)),那么下一个玩家可以立即从该矩形中切出一个 \(1 \times 1\) 的格子从而直接获胜。因此,在最优策略下,任何玩家都不会主动切出维度为 1 的矩形,除非当前矩形无法再进行“安全”的切割(即切出的两个部分边长都 \(\ge 2\))。此时,游戏等价于在一个“安全移动”集合上的博弈,当一名玩家无法再将矩形切成两个边长均 \(\ge 2\) 的小矩形时,他将不得不切出一个维度为 1 的矩形,从而输掉游戏。

该游戏是一个典型的公平组合游戏,可以使用 SG 定理求解。定义状态 \(\text{SG}(w,h)\) 表示宽度为 \(w\)、高度为 \(h\) 的矩形的博弈状态值。切割一个矩形会将原游戏分解为两个独立的子游戏,根据 SG 定理,一个移动的后继状态的 SG 值为两个子状态 SG 值的异或和。

\[\text{SG}(w, h) = \text{mex}(\{\text{SG}(k, h) \oplus \text{SG}(w-k, h) \mid 2 \le k \le w-2\} \cup \{\text{SG}(w, k) \oplus \text{SG}(w, h-k) \mid 2 \le k \le h-2\}) \]

对于 \(w \lt 4\)\(h \lt 4\) 的矩形,如果它们无法被切割成两个边长均 \(\ge 2\) 的矩形,则其 \(SG\) 值为 0。

由于 \(W,H \le 200\),可以预处理出所有可能的 \(\text{SG}(w,h)\) 值。对于每个状态,遍历所有可能的垂直切割位置和水平切割位置,记录出现的异或和,取最小未出现的非负整数。预处理完成后,对于每个测试用例,直接查询 \(\text{SG}(W,H)\),若其值大于 0,则先手必胜,否则先手必败。

时间复杂度为 \(O(W \times H \times (W+H))\)

参考代码
#include <cstdio>
int sg[205][205];
bool vis[512]; // 标记后继状态的 SG 异或和
// 预计算 200*200 范围内所有矩形的 SG 值
void init() {
    for (int i = 2; i <= 200; i++) {
        for (int j = 2; j <= 200; j++) {
            for (int k = 0; k < 512; k++) vis[k] = false;
            // 垂直切割:将宽度 i 切成 k 和 i-k,保证两个新矩形的宽度都 >= 2
            for (int k = 2; k <= i - k; k++) {
                vis[sg[k][j] ^ sg[i - k][j]] = true;
            }
            // 水平切割:将高度 j 切成 k 和 j-k,保证两个新矩形的高度都 >= 2
            for (int k = 2; k <= j - k; k++) {
                vis[sg[i][k] ^ sg[i][j - k]] = true;
            }
            // 计算 mex (最小未出现的非负整数)
            int k = 0;
            while (vis[k]) k++;
            sg[i][j] = k;
        }
    }
}
int main()
{
    init(); // 预处理 SG 表
    int w, h;
    while (scanf("%d%d", &w, &h) == 2) {
        // SG 值大于 0 表示先手必胜,否则必败
        if (sg[w][h] > 0) printf("WIN\n");
        else printf("LOSE\n");
    }
    return 0;
}
posted @ 2026-04-03 21:25  RonChen  阅读(4)  评论(0)    收藏  举报