news 2026/6/26 6:02:26

SG函数:让博弈“化整为零”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SG函数:让博弈“化整为零”

引言

在算法竞赛中,博弈论题目常常让人望而生畏:两个绝顶聪明的人轮流操作,问谁赢谁输。最简单的取石子游戏(Nim 游戏)有一个漂亮的结论——异或和为 0 则先手必败,否则先手必胜。但题目稍微一变,比如“可以把一堆石子分成两堆”,这个结论就不够用了。

SG 函数(Sprague-Grundy Function)正是为了统一解决这类公平组合游戏而生的。它将复杂的博弈局面映射为一个非负整数(SG 值),然后通过SG 定理将多个子游戏的组合转化为 Nim 和(异或和)。掌握 SG 函数,你就掌握了解决大部分公平组合游戏的通法。

如果说 Nim 游戏是一把“钥匙”,那么 SG 函数就是“万能钥匙胚”——任何公平组合游戏,只要算出每个局面的 SG 值,就能用 Nim 和的思路判断胜负。


前置知识

在学习 SG 函数之前,你需要掌握以下基础:

  1. 公平组合游戏(ICG)的定义:

    • 两名玩家轮流行动。

    • 任何状态下,两名玩家的合法操作集合完全相同

    • 不能行动者输。

    • 游戏一定在有限步内结束

  2. 必胜态与必败态

    • 没有后继状态的状态是必败态(P-position)

    • 存在至少一个后继状态为必败态的状态是必胜态(W-position)

    • 所有后继状态都是必胜态的状态是必败态

  3. mex 运算mex(S)表示不属于集合 S 的最小非负整数。例如mex({0, 2, 4}) = 1


第一章:SG函数的定义与计算

1.1 SG 函数的定义

对于公平组合游戏中的状态 xx,其 SG 函数值定义为:

SG(x)=mex({SG(y)∣y 是 x 的后继状态})SG(x)=mex({SG(y)∣y 是 x 的后继状态})

也就是说,一个状态的 SG 值等于其所有后继状态的 SG 值集合中未出现的最小非负整数

理解 SG 函数的一个关键视角:SG(x) = k 意味着从状态 x 可以转移到 SG 值为 0, 1, ..., k-1 的所有状态。这使得 SG 值与 Nim 游戏中的一堆石子形成了完美的对应关系。

1.2 计算 SG 函数的步骤

第一步:确定状态的表示
明确每个博弈状态如何表示。例如 Nim 游戏中,状态就是“各堆石子的数量”。

第二步:画出博弈图(有向无环图)
从终止状态开始,递推计算每个状态的 SG 值。

第三步:应用 mex 运算
对于每个状态,收集所有后继状态的 SG 值,取 mex。

第四步:利用 SG 定理组合
如果游戏由多个独立子游戏组成,总 SG 值为各子游戏 SG 值的异或和。

1.3 手算示例:简单的取石子游戏

游戏规则:一堆石子,每次可以取 1 颗或 3 颗,取到最后一颗的人赢。

计算 SG 值:

  • SG(0)=0(没有石子可取,必败)

  • SG(1)=mex({SG(0)})=mex({0})=1

  • SG(2)=mex({SG(1)})=mex({1})=0

  • SG(3)=mex({SG(2),SG(0)})=mex({0,0})=mex({0})=1

  • SG(4)=mex({SG(3),SG(1)})=mex({1,1})=0

规律:SG(x)=0SG(x)=0 当且仅当 xx 是偶数。

这个例子说明:SG 值为 0 的状态对应必败态,SG 值不为 0 的状态对应必胜态


第二章:SG定理与Nim游戏

2.1 SG 定理

SG 定理(Sprague-Grundy Theorem)是公平组合游戏理论的基石:

一个组合游戏(由若干子游戏组成)的 SG 值,等于各子游戏 SG 值的异或和(Nim 和)

换句话说,如果游戏 GG 可以分解为若干互不影响的子游戏 G1,G2,...,GnG1​,G2​,...,Gn​,那么:

SG(G)=SG(G1)⊕SG(G2)⊕⋯⊕SG(Gn)

胜负判定

  • 若 SG(G)=0,则先手必败

  • 若 SG(G)≠0,则先手必胜

这正是 Nim 游戏结论的推广:Nim 游戏中每堆石子就是一个子游戏,SG(一堆石子)=石子数SG(一堆石子)=石子数,所以总 SG 值就是所有堆石子数的异或和。

2.2 SG 定理的直观理解

为什么 SG 值可以异或?因为 SG 值k意味着该状态可以转移到 SG 值为 0, 1, ..., k-1 的任意状态。这和 Nim 游戏中一堆 k 颗石子的性质完全一样——你可以把它变成 0, 1, ..., k-1 颗。

因此,整个游戏等价于一个 Nim 游戏,其中第 ii 堆石子的数量就是第 ii 个子游戏的 SG 值。


第三章:性质与复杂度

性质说明
SG = 0 必败SG 值为 0 的状态是必败态
SG ≠ 0 必胜SG 值不为 0 的状态是必胜态
SG 定理总 SG = 各子游戏 SG 的异或和
计算方式从终止状态开始递推,或 DFS + 记忆化
适用场景所有公平组合游戏
复杂度O(状态数×转移数)O(状态数×转移数)

第四章:例题与详细解析

例题1:Nim游戏 —— 洛谷 P2197

题目描述
地上有 nn 堆石子,每堆石子数量为 aiai​。两人轮流操作,每次可以从任意一堆中取出任意数量的石子(至少取 1 颗,可以取完)。不能操作者输。判断先手是否必胜。

输入示例

1 3 1 2 3

输出示例

Yes

解题思路
这是 Nim 游戏的模板题。每堆石子是一个独立的子游戏,SG(一堆 x 颗石子)=xSG(一堆 x 颗石子)=x。根据 SG 定理,总 SG 值为所有堆石子数的异或和。

详细解析

第一步:认识 Nim 游戏的结论

Nim 游戏有一个著名的定理:先手必胜当且仅当所有堆石子数的异或和不为 0

证明思路(简要):

  • 若异或和为 0,无论先手如何操作,异或和都会变为非 0,后手总能把异或和重新变为 0。

  • 若异或和非 0,先手总能找到一堆石子,从中取出若干颗,使异或和变为 0。

  • 最终所有堆石子数为 0 时,异或和为 0,轮到谁谁输。

第二步:代码实现

#include <bits/stdc++.h> using namespace std; int main() { int T; cin >> T; while (T--) { int n; cin >> n; int xorsum = 0; for (int i = 0; i < n; i++) { int a; cin >> a; xorsum ^= a; } cout << (xorsum ? "Yes" : "No") << '\n'; } return 0; }

第三步:复杂度分析

时间复杂度 O(n)O(n),空间复杂度 O(1)O(1)。


例题2:高手过招 —— 洛谷 P2575

题目描述
有一个 n×20n×20 的棋盘,每个格子有棋子(1)或没有棋子(0)。每次操作:选择一个棋子,将它向右移动到第一个空格处。如果某个棋子右边没有空格,则不能移动该棋子。当所有棋子都不能移动时,游戏结束,不能操作者输。判断先手是否必胜。

输入示例

2 3 1 2 3 2 4 5

输出示例

YES

解题思路
这道题不能直接套用 Nim 游戏的结论,需要先将其转化为阶梯 Nim(Staircase Nim)问题。

详细解析

第一步:观察游戏性质

每一行是独立的子游戏,总 SG 值为各行 SG 值的异或和。

对于一行 20 个格子,从右往左看,连续的棋子构成“一段”。将相邻的有棋子的格子合并为一个“阶梯”,阶梯上的棋子数等于该段连续棋子的长度。

第二步:转化为阶梯 Nim

阶梯 Nim 的结论是:只看奇数层阶梯上的石子数,异或和不为 0 则先手必胜

为什么?因为移动一个棋子到右边的空格,等价于把石子从当前阶梯移到更低的阶梯。这和阶梯 Nim 的规则完全一致。

第三步:代码实现

#include <bits/stdc++.h> using namespace std; int main() { int T; cin >> T; while (T--) { int n; cin >> n; int ans = 0; for (int i = 0; i < n; i++) { int x; cin >> x; vector<int> pos(20, 0); for (int j = 0; j < x; j++) { int p; cin >> p; pos[p-1] = 1; // 标记有棋子的位置 } // 从右往左处理,转化为阶梯 Nim int cnt = 0, step = 0; for (int j = 19; j >= 0; j--) { if (pos[j] == 0) { step++; // 空格增加,进入下一级阶梯 } else { if (step % 2 == 1) cnt++; // 奇数层阶梯上的棋子数 } } ans ^= cnt; // 每行的 SG 值异或起来[reference:76] } cout << (ans ? "YES" : "NO") << '\n'; } return 0; }

第四步:复杂度分析

每行 20 个格子,nn 行,时间复杂度 O(20n)O(20n),空间复杂度 O(1)O(1)。


第五章:常见变体与应用场景

变体核心思路典型例题
Nim 游戏所有堆异或和P2197
阶梯 Nim只统计奇数层阶梯P2575
Anti-Nim取到最后一颗石子的人输需特殊判断
拆分 Nim可将一堆分成两堆,SG 打表找规律HDU 3032
树上博弈转化为阶梯 Nim 或 SG 函数各类树上删边游戏
SG 打表找规律小范围计算 SG,观察周期规律HDU 5795

总结

SG 函数是解决公平组合游戏问题的统一框架。它的核心流程是:

  1. 确定状态:明确每个局面如何表示。

  2. 计算 SG 值:从终止状态开始,利用mex运算递推。

  3. 应用 SG 定理:将总游戏的 SG 值分解为各子游戏 SG 值的异或和。

  4. 判断胜负:SG 值为 0 则必败,否则必胜。

从 P2197(Nim 游戏模板)的“直接异或”到 P2575(高手过招)的“转化为阶梯 Nim”,SG 函数的价值在于化繁为简——无论游戏规则多么复杂,只要能分解为若干独立的子游戏,就能用 SG 定理统一求解

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 6:00:28

软件逆向工程中的脱壳技术:从原理到实战应用

1. 逆向分析中的“脱壳”&#xff1a;从概念到实战逆向分析&#xff0c;听起来像是个高深莫测的黑客术语&#xff0c;其实它更像是一场精密的数字考古。我们面对的不是古老的文物&#xff0c;而是经过层层包装的软件程序。而“脱壳”&#xff0c;就是这场考古中最核心、也最富挑…

作者头像 李华
网站建设 2026/6/26 6:00:08

基于交织团设计的分布式任务分配:从Steiner系统到工程实践

1. 项目概述&#xff1a;当分布式计算遇上组合数学在分布式计算领域&#xff0c;任务分配一直是个核心且棘手的问题。想象一下&#xff0c;你管理着一个庞大的数据中心&#xff0c;里面有成千上万台服务器&#xff0c;每天要处理海量的计算任务&#xff0c;比如视频转码、科学模…

作者头像 李华
网站建设 2026/6/26 5:59:47

为什么越来越多三甲医院,深度选用语音通信系统?

为什么越来越多三甲医院&#xff0c;深度选用 TJWZ 五洲科技语音通信系统&#xff1f; 不是偶然&#xff0c;是长期贴合医疗刚需的实力选择✅1️⃣ 极致稳定&#xff0c;保障生命级通信 医院急诊、ICU、手术室容不得通话中断。TJWZ 自研软交换架构支持双机热备、线路冗余&#…

作者头像 李华
网站建设 2026/6/26 5:57:59

C++变量、数据类型与输入输出总结

一、前言变量和数据类型是C最基础的内容&#xff0c;也是所有程序逻辑的基石。熟练掌握基础类型、输入输出规范&#xff0c;能有效减少新手写代码时的语法错误。二、C常用基础数据类型- int&#xff1a;整型 ​ - double&#xff1a;浮点型 ​ - char&#xff1a;字符型 ​ - b…

作者头像 李华
网站建设 2026/6/26 5:56:14

STM32单片机智能无线点餐系统无线APP彩屏168-1(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_可以扫码

STM32单片机智能无线点餐系统无线APP彩屏168-1(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_可以扫码 产品功能描述&#xff1a; 本系统由STM32F103C8T6单片机核心板、无线蓝牙/WIFI模块-可选、TFT1.44寸彩屏液晶显示电路、蜂鸣器报警驱动电路…

作者头像 李华