SG函数:让博弈“化整为零”
引言在算法竞赛中博弈论题目常常让人望而生畏两个绝顶聪明的人轮流操作问谁赢谁输。最简单的取石子游戏Nim 游戏有一个漂亮的结论——异或和为 0 则先手必败否则先手必胜。但题目稍微一变比如“可以把一堆石子分成两堆”这个结论就不够用了。SG 函数Sprague-Grundy Function正是为了统一解决这类公平组合游戏而生的。它将复杂的博弈局面映射为一个非负整数SG 值然后通过SG 定理将多个子游戏的组合转化为 Nim 和异或和。掌握 SG 函数你就掌握了解决大部分公平组合游戏的通法。如果说 Nim 游戏是一把“钥匙”那么 SG 函数就是“万能钥匙胚”——任何公平组合游戏只要算出每个局面的 SG 值就能用 Nim 和的思路判断胜负。前置知识在学习 SG 函数之前你需要掌握以下基础公平组合游戏ICG的定义两名玩家轮流行动。任何状态下两名玩家的合法操作集合完全相同。不能行动者输。游戏一定在有限步内结束。必胜态与必败态没有后继状态的状态是必败态P-position。存在至少一个后继状态为必败态的状态是必胜态W-position。所有后继状态都是必胜态的状态是必败态。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})1SG(2)mex({SG(1)})mex({1})0SG(3)mex({SG(2),SG(0)})mex({0,0})mex({0})1SG(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(状态数×转移数)第四章例题与详细解析例题1Nim游戏 —— 洛谷 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 游戏的结论需要先将其转化为阶梯 NimStaircase 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; vectorint 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只统计奇数层阶梯P2575Anti-Nim取到最后一颗石子的人输需特殊判断拆分 Nim可将一堆分成两堆SG 打表找规律HDU 3032树上博弈转化为阶梯 Nim 或 SG 函数各类树上删边游戏SG 打表找规律小范围计算 SG观察周期规律HDU 5795总结SG 函数是解决公平组合游戏问题的统一框架。它的核心流程是确定状态明确每个局面如何表示。计算 SG 值从终止状态开始利用mex运算递推。应用 SG 定理将总游戏的 SG 值分解为各子游戏 SG 值的异或和。判断胜负SG 值为 0 则必败否则必胜。从 P2197Nim 游戏模板的“直接异或”到 P2575高手过招的“转化为阶梯 Nim”SG 函数的价值在于化繁为简——无论游戏规则多么复杂只要能分解为若干独立的子游戏就能用 SG 定理统一求解

相关新闻

视神经里的“守护者”:云克隆小鼠视神经星形胶质细胞(Optic Nerve Astrocytes,ONA)让青光眼研究有了新工具

视神经里的“守护者”:云克隆小鼠视神经星形胶质细胞(Optic Nerve Astrocytes,ONA)让青光眼研究有了新工具

在视神经系统中,有一群细胞虽然不像神经元那样直接传递视觉信号,却默默承担着“守护者”的职责——星形胶质细胞(Optic Nerve Astrocytes ,ONA)。它们是中枢神经系统中数量最多的胶质细胞,在视神经乳头处更是主力军。视…

2026/6/26 6:02:49阅读更多 →
LabVIEW汽车控制板自动测试系

LabVIEW汽车控制板自动测试系

阅读时间:6分钟 | 适用人群:汽车电子工程师/测试系统设计师/质量控制技术人员汽车控制板作为整车电子电气架构的核心单元,传统人工测试依赖示波器、万用表等独立仪器,存在测试周期长、重复性差、数据追溯困难等问题。某汽车零部件…

2026/6/26 6:02:49阅读更多 →
为何要服务好每月3K需求的IoT FEM客户

为何要服务好每月3K需求的IoT FEM客户

转载自--《钟林谈芯》很明显,2026年需要做技术支持和调试的客户板子增加了很多,FAE忙不过来了。那就加人,今年从福州大学微电子学院招聘了本科毕业生,加强技术支持团队。其实,就算不招聘新的FAE,三伍微也能…

2026/6/26 6:02:49阅读更多 →
openYuanrong frontend:云原生函数网关的终极解决方案 [特殊字符]

openYuanrong frontend:云原生函数网关的终极解决方案 [特殊字符]

openYuanrong frontend:云原生函数网关的终极解决方案 🚀 【免费下载链接】yuanrong-frontend openYuanrong frontend:openYuanrong 网关,支持函数创建、调用等功能 项目地址: https://gitcode.com/openeuler/yuanrong-frontend…

2026/6/26 7:12:54阅读更多 →
从寄存器角度理解 Type-C 上电与下电:两种控制方式解析

从寄存器角度理解 Type-C 上电与下电:两种控制方式解析

1. 项目背景在嵌入式 Linux 开发中,很多外设并不是系统启动后就一直保持供电。例如 USB Type-C 接口、外部模组、电源芯片、通信模块等,通常会通过一个电源使能引脚进行控制。这个使能引脚一般由 GPIO 控制。当 GPIO 输出高电平时,电源开关芯…

2026/6/26 7:12:54阅读更多 →
Java基础:String、StringBuilder 和 StringBufferr对比

Java基础:String、StringBuilder 和 StringBufferr对比

目录 基础用法 1.String 2.StringBuilder和StringBufferr 略微深入 1.为什么StringBuiler线程不安全 2.为什么StringBuffer线程安全 基础用法 1.String 在Java中,String是不可变类。 所以new一个String对象之后,它的值是不可变的。对它的修改&a…

2026/6/26 7:12:54阅读更多 →
电磁流量计选型指南:精准匹配工况需求,保障工业测量可靠性

电磁流量计选型指南:精准匹配工况需求,保障工业测量可靠性

引言:工业测量基石的选型挑战 在现代工业自动化与智能化浪潮中,过程控制仪表作为感知系统的关键组成部分,其性能直接决定了生产流程的安全性、效率和产品质量。其中,电磁流量计凭借无机械运动部件、测量精度高、适用介质广泛等优势…

2026/6/26 7:12:54阅读更多 →
数仓建模理论

数仓建模理论

因为工作原因,小黄需要涉入大数据这一块的工作,所以再次补习一下数仓建模这一块的理论,参考《阿里大数据之路》这本书,以及AI来给我讲解的方式进行学习。 什么是数仓建模 我觉得是这样,数仓整套工作分为数据存储和数据…

2026/6/26 7:12:54阅读更多 →
阿里云Linux云服务器部署Python项目:从零到上线的完整实战指南

阿里云Linux云服务器部署Python项目:从零到上线的完整实战指南

一、部署前的准备:选型与规划 在开始部署之前,需要做好充分的准备工作。这包括选择合适的云服务器配置、规划网络与安全策略,以及准备本地开发环境。良好的前期规划能够避免后续部署过程中的许多麻烦。 1.1 选择阿里云ECS实例 阿里云ECS&a…

2026/6/26 7:07:53阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/25 9:39:54阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/26 4:15:25阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/25 9:01:34阅读更多 →
HPE (慧与) 服务器专用 ESXi 9 全套官方定制资源详解 + 完整部署升级教程

HPE (慧与) 服务器专用 ESXi 9 全套官方定制资源详解 + 完整部署升级教程

一、前言:企业运维痛点与资源价值自博通收购 VMware 之后,原 VMware 公开免费下载渠道全面关闭,企业运维人员想要获取适配 HPE 慧与服务器的 ESXi 9 原厂镜像,必须注册博通账号、绑定有效授权才能下载,无授权账号无法获…

2026/6/26 0:02:15阅读更多 →
Kotlin的@JvmStatic与@JvmField:与Java互操作的注解

Kotlin的@JvmStatic与@JvmField:与Java互操作的注解

Kotlin作为一门现代编程语言,与Java的互操作性一直是其核心优势之一。为了让Kotlin代码能够无缝对接Java,Kotlin提供了多种注解来优化互操作体验,其中JvmStatic和JvmField是两个关键注解。它们分别用于解决静态成员和字段在Java中的访问问题&…

2026/6/26 0:02:15阅读更多 →
深入解析musl libc中的mmap实现源码

深入解析musl libc中的mmap实现源码

最近在阅读musl libc源码时,发现其mmap的实现非常精妙,特分享给大家。 一、代码整体结构 这段代码实现了__mmap函数,并通过weak_alias导出为mmap。这是典型的musl libc风格——提供弱符号以便用户可以重写。 weak_alias(__mmap, mmap); 二…

2026/6/26 0:02:15阅读更多 →