公平组合游戏 前置知识:博弈论简介 
本文讨论 公平组合游戏 。
公平组合游戏中,最基础也最重要的是正常 Nim 游戏。Sprague–Grundy 定理指出,所有正常规则的公平组合游戏都等价于一个单堆 Nim 游戏。由此,可以发展出 Sprague–Grundy 函数和 Nim 数的概念,它们完全地刻画了一个正常规则的公平组合游戏。因此,本文首先建立了正常 Nim 游戏的结论和 Sprague–Grundy 理论。随后,本文讨论了算法竞赛中常见的一些公平组合游戏。
最后,本文简单地讨论了反常 Nim 游戏。反常游戏相对于正常游戏来说要复杂得多,也很少在算法竞赛中出现。本文提到的游戏,如果没有特别说明,均默认为正常的公平组合游戏。
「状态」、「局面」与「游戏」  本文会交替地使用这三个词语。在博弈论中,游戏的状态(state)通常包括到游戏的某一时刻为止,所有可能与游戏有关的信息。在一般的情形下,游戏的状态通常包括双方玩家过往的行动、已经实现的随机变量值、双方已知信息的内容等。游戏的局面(position)相对来说并非博弈论的标准术语,通常指在游戏的某一时刻,双方玩家面对的局势,例如棋类游戏中各棋子的位置等。仅对于公平组合游戏(或更一般的零和、确定、完美信息游戏)而言,由于游戏不涉及随机性,且玩家未来的行动集合与收益函数均与到达当前局面的历史路径(即之前双方的行为)无关,所以,游戏的状态(state)和局面(position)没有区别,且都可以看作博弈图上的一个结点(node)。由于一个游戏(game)总是可以由它的初始局面描述,所以有时也会直接使用「局面」一词代指游戏本身。
Nim 游戏 Nim 游戏的规则很简单:
Nim 游戏  共有 𝑛 n 𝑖 i 𝑎 𝑖 a i 
容易验证,Nim 游戏是正常规则的公平组合游戏。
例子  举个例子。当前,有 3 3 2 , 5 , 4 2 , 5 , 4 1 1 2 2 0 , 5 , 4 0 , 5 , 4 2 2 4 4 2 , 1 , 4 2 , 1 , 4 0 , 0 , 5 0 , 0 , 5 3 3 5 5 
博弈图和状态 Nim 游戏中,局面可能的变化可以用博弈图来描述。
将每一个可能的状态都看作是图中的一个结点,并将状态向它的后继状态(即通过一次操作可以达到的状态)连边,就得到一个有向无环图,这就是博弈图。图是无环的,因为 Nim 游戏中,每次操作,石子的总数量都是严格减少的。
例子  例如,对于初始局面有 3 3 1 , 1 , 2 1 , 1 , 2 
 
 马上就会提到,图中的红色结点表示必胜状态,黑色结点表示必败状态。
由于 Nim 游戏是公平组合游戏,每个玩家是否有必胜策略,只取决当前游戏所处的状态,而与玩家的身份无关。因此,所有状态可以分为(先手)必胜状态  和(先手)必败状态 ,分别记为 N N P P 
通过下述引理,可以归纳地将所有状态标记为必胜状态和必败状态:
引理  正常规则的公平组合游戏中,
 没有后继状态的状态是必败状态 P P  一个状态是必胜状态 N N P P  一个状态是必败状态 P P N N  证明  对于第一条,如果玩家当前已经没有可选的行动,那么玩家已经输掉了游戏。
 对于第二条,如果该状态至少有一个后继状态为必败状态,那么玩家可以操作到该必败状态;此时,对手面临了先手必败状态,玩家自己就获得了胜利。
 对于第三条,如果不存在一个后继状态为必败状态,那么无论如何,玩家只能操作到必胜状态;此时,对手面临了先手必胜状态,玩家自己就输掉了游戏。
所有公平组合游戏中,博弈图都是有向无环图。所以,通过这三条性质,可以在绘制出博弈图后,在 𝑂 ( | 𝑉 |   + | 𝐸 | ) O ( | V | + | E | ) | 𝑉 | | V | | 𝐸 | | E | 
这一引理可以推广到反常游戏和有向图可能有环的情形。相关讨论详见 有向图游戏  一节。
Nim 和 继续考察 Nim 游戏。
通过绘制博弈图,可以在 Ω ( ∏ 𝑛 𝑖 = 1 𝑎 𝑖 ) Ω ( ∏ i = 1 n a i ) 
Nim 和  自然数 𝑎 1 , 𝑎 2 , ⋯ , 𝑎 𝑛 a 1 , a 2 , ⋯ , a n Nim 和 (Nim sum)定义为 𝑎 1   ⊕ 𝑎 2   ⊕ ⋯   ⊕ 𝑎 𝑛 a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n 
所谓 Nim 和,就是 异或运算 。
定理  Nim 游戏中,状态 ( 𝑎 1 , 𝑎 2 , ⋯ , 𝑎 𝑛 ) ( a 1 , a 2 , ⋯ , a n ) P P 
 𝑎 1 ⊕ 𝑎 2 ⊕ ⋯ ⊕ 𝑎 𝑛 = 0 . a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n = 0. 证明  对所有可能的状态应用归纳法:
 如果 𝑎 𝑖   = 0 a i = 0 𝑖   = 1 , ⋯ , 𝑛 i = 1 , ⋯ , n 0 0  如果 𝑘   = 𝑎 1   ⊕ 𝑎 2   ⊕ ⋯   ⊕ 𝑎 𝑛   ≠ 0 k = a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n ≠ 0 𝑎 ′ 1   ⊕ 𝑎 ′ 2   ⊕ ⋯   ⊕ 𝑎 ′ 𝑛   = 0 a 1 ′ ⊕ a 2 ′ ⊕ ⋯ ⊕ a n ′ = 0 𝑎 𝑖 a i 𝑎 𝑖   ⊕ 𝑘 a i ⊕ k 𝑎 𝑖   > 𝑎 𝑖   ⊕ 𝑘 a i > a i ⊕ k 
 实际上,设 𝑘 k 1 1 𝑑 d 𝑎 𝑖 a i 𝑑 d 1 1 𝑎 𝑖   > 𝑎 𝑖   ⊕ 𝑘 a i > a i ⊕ k 𝑎 𝑖   ⊕ 𝑘 a i ⊕ k 𝑑 d 0 0 𝑎 𝑖 a i 
如果 𝑎 1   ⊕ 𝑎 2   ⊕ ⋯   ⊕ 𝑎 𝑛   = 0 a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n = 0 0 0 𝑎 𝑖 a i 𝑎 ′ 𝑖   ≠ 𝑎 𝑖 a i ′ ≠ a i 𝑎 ′ 𝑖   ⊕ 𝑎 𝑖   ≠ 0 a i ′ ⊕ a i ≠ 0 
由此,可以在 𝑂 ( 𝑛 ) O ( n ) 
Sprague–Grundy 理论 Sprague–Grundy 理论指出,所有公平组合游戏都等价于单堆 Nim 游戏。这一结论主要应用的场景,就是游戏由多个相互独立的子游戏组成的情形。此时,游戏的状态判定可以通过计算子游戏的 SG 函数值的 Nim 和来完成。如果游戏本身没有这样的结构,那么,判定必胜状态和必败状态只需要应用前文博弈图一节的 引理 。
游戏的记法 前文已经说明,所有公平组合游戏都可以通过绘制博弈图来描述。由于博弈图中,每个状态的性质只由它的后继状态决定,所以,可以将博弈图中的一个状态 𝑆 S 
例子(续)  以上文的博弈图为例,可以得到如下状态表示:
 𝑆 0 , 0 , 0 = { } , 𝑆 0 , 1 , 0 = { 𝑆 0 , 0 , 0 } = { { } } , 𝑆 0 , 0 , 1 = { 𝑆 0 , 0 , 0 } = { { } } , 𝑆 0 , 0 , 2 = { 𝑆 0 , 0 , 0 , 𝑆 0 , 0 , 1 } = { { } , { { } } } , 𝑆 0 , 1 , 1 = { 𝑆 0 , 0 , 0 , 𝑆 0 , 1 , 0 , 𝑆 0 , 0 , 1 } = { { } , { { } } } , 𝑆 0 , 1 , 2 = { 𝑆 0 , 0 , 2 , 𝑆 0 , 1 , 0 , 𝑆 0 , 1 , 1 } = { { { } } , { { } , { { } } } } . S 0 , 0 , 0 = { } , S 0 , 1 , 0 = { S 0 , 0 , 0 } = { { } } , S 0 , 0 , 1 = { S 0 , 0 , 0 } = { { } } , S 0 , 0 , 2 = { S 0 , 0 , 0 , S 0 , 0 , 1 } = { { } , { { } } } , S 0 , 1 , 1 = { S 0 , 0 , 0 , S 0 , 1 , 0 , S 0 , 0 , 1 } = { { } , { { } } } , S 0 , 1 , 2 = { S 0 , 0 , 2 , S 0 , 1 , 0 , S 0 , 1 , 1 } = { { { } } , { { } , { { } } } } . 其中,𝑆 0 , 1 , 0   = 𝑆 0 , 0 , 1 S 0 , 1 , 0 = S 0 , 0 , 1 𝑆 0 , 0 , 2   = 𝑆 0 , 1 , 1 S 0 , 0 , 2 = S 0 , 1 , 1 
一个游戏可以用它的初始状态表示。
尽管公平游戏的表示可能相当复杂,单堆 Nim 游戏相对来说简单很多。只有一堆石子,石子数量为 𝑛 n 
∗ 0 = { } ,   ∗ 𝑛 = { ∗ 𝑚 : 𝑚 < 𝑛 ,   𝑚 ∈ 𝐍 } = { ∗ 0 , ∗ 1 , ⋯ , ∗ ( 𝑛 − 1 ) } . ∗ 0 = { } ,   ∗ n = { ∗ m : m < n ,   m ∈ N } = { ∗ 0 , ∗ 1 , ⋯ , ∗ ( n − 1 ) } . 其中,记号 ∗ 𝑛 ∗ n 𝑛 n 
例子(续)  利用这一记号,上面的例子中的状态可以简单地表示为
 𝑆 0 , 0 , 0 = ∗ 0 ,   𝑆 0 , 1 , 0 = 𝑆 0 , 0 , 1 = ∗ 1 ,   𝑆 0 , 0 , 2 = 𝑆 0 , 1 , 1 = ∗ 2 ,   𝑆 0 , 1 , 2 = { ∗ 1 , ∗ 2 } . S 0 , 0 , 0 = ∗ 0 ,   S 0 , 1 , 0 = S 0 , 0 , 1 = ∗ 1 ,   S 0 , 0 , 2 = S 0 , 1 , 1 = ∗ 2 ,   S 0 , 1 , 2 = { ∗ 1 , ∗ 2 } . 在随后的讨论中,记号 𝑇   ∈ 𝑆 T ∈ S 𝑇 T 𝑆 S 
游戏的和与等价 游戏的等价关系,依赖于游戏的和
游戏的和  游戏 𝐺 G 𝐻 H 和 (sum),或称 游戏组合 (combined game),记作 𝐺   + 𝐻 G + H 
 𝐺 + 𝐻 = { 𝑔 + 𝐻 : 𝑔 ∈ 𝐺 } ∪ { 𝐺 + ℎ : ℎ ∈ 𝐻 } . G + H = { g + H : g ∈ G } ∪ { G + h : h ∈ H } . 游戏的和,可以理解为由两个同时进行且互不干扰的子游戏组成的游戏,玩家在每一步能且只能选择其中一个子游戏移动一步,且游戏在两个子游戏都无法移动时结束。游戏的和的概念,可以推广到任意多个游戏的情形,且满足结合律和交换律——也就是说,多个游戏组合的结果,和组合进行的次序以及游戏的顺序都无关。Nim 游戏就是多个单堆 Nim 游戏的和。
一个观察是,尽管单堆 Nim 游戏中,除了没有石子的情形,都是先手必胜状态,但是这些不同的单堆 Nim 游戏在和其他的单堆 Nim 游戏组合起来时,得到的游戏并不相同。比如,游戏 ∗ 𝑛 ∗ n ∗ 𝑛 ∗ n ∗ 𝑛 ′   ≠   ∗ 𝑛 ∗ n ′ ≠ ∗ n 
这个观察带来的启示是,可以通过考察与其他游戏的和来研究某个游戏的性质。这就引出了游戏的等价的概念。
游戏的等价关系  如果对于所有游戏 𝐻 H 𝐺 1   + 𝐻 G 1 + H 𝐺 2   + 𝐻 G 2 + H 𝐺 1 G 1 𝐺 2 G 2 等价 (equivalent),记作 𝐺 1   ≈ 𝐺 2 G 1 ≈ G 2 
容易验证,这样定义的 ≈ ≈ 等价关系 。
Sprague–Grundy 函数 对 Nim 游戏的分析说明,不同的单堆 Nim 游戏互不等价。但是,所有的公平游戏都等价于某个单堆 Nim 游戏。由此,可以给每个公平游戏都分配一个数字,这就是 Sprague–Grundy 函数。
为了证明这些结论,首先需要建立关于游戏等价关系的两个引理。第一,将必败游戏和任何游戏组合到一起,都和原来的游戏等价。
引理 1  对于游戏 𝐺 G 𝐴   ∈ P A ∈ P 𝐺   ≈ 𝐺   + 𝐴 G ≈ G + A 
证明  按照定义,只需要证明对于任何游戏 𝐻 H 𝐺   + 𝐻   ≈ 𝐺   + 𝐴   + 𝐻 G + H ≈ G + A + H 
 如果游戏 𝐺   + 𝐻 G + H 𝐺   + 𝐴   + 𝐻 G + A + H 𝐴 A 𝐺   + 𝐻 G + H 
 如果游戏 𝐺   + 𝐻 G + H 𝐺   + 𝐴   + 𝐻 G + A + H 𝐺   + 𝐻 G + H 𝐴 A 
第二,两个游戏等价,当且仅当它们的和是必败游戏。这一引理提供了证明两个游戏等价的方法。
引理 2  游戏 𝐺 G 𝐺 ′ G ′ 𝐺   + 𝐺 ′   ∈ P G + G ′ ∈ P 
证明  如果游戏 𝐺 G 𝐺 ′ G ′ 𝐺   + 𝐺 ′   ≈ 𝐺   + 𝐺 G + G ′ ≈ G + G 𝐺   + 𝐺 G + G 
 反过来,如果 𝐺   + 𝐺 ′ G + G ′ 𝐺   ≈ 𝐺   + ( 𝐺   + 𝐺 ′ )   = ( 𝐺   + 𝐺 )   + 𝐺 ′   ≈ 𝐺 ′ G ≈ G + ( G + G ′ ) = ( G + G ) + G ′ ≈ G ′ 
利用这些引理,可以得到如下定理:
定理(Sprague–Grundy)  对于任何一个(有限)公平游戏 𝐺 G 𝑛   ∈ 𝐍 n ∈ N 𝐺   ≈   ∗ 𝑛 G ≈ ∗ n 
证明  要证明定理的结论,可以应用数学归纳法。设游戏 𝐺   = { 𝐺 1 , 𝐺 2 , ⋯ , 𝐺 𝑘 } G = { G 1 , G 2 , ⋯ , G k } 𝑛 1 , 𝑛 2 , ⋯ , 𝑛 𝑘 n 1 , n 2 , ⋯ , n k 𝐺 𝑖   ≈   ∗ 𝑛 𝑖 G i ≈ ∗ n i 
 𝐺 ′ = { ∗ 𝑛 1 , ∗ 𝑛 2 , ⋯ , ∗ 𝑛 𝑘 } . G ′ = { ∗ n 1 , ∗ n 2 , ⋯ , ∗ n k } . 将要证明的是,𝐺 ′   ≈   ∗ 𝑚 G ′ ≈ ∗ m 𝑚   = m e x  { 𝑛 1 , 𝑛 2 , ⋯ , 𝑛 𝑘 } m = mex  { n 1 , n 2 , ⋯ , n k } 
 第一步,需要说明 𝐺   ≈ 𝐺 ′ G ≈ G ′ 引理 2 ,只需要证明游戏 𝐺   + 𝐺 ′ G + G ′ 𝐺   ≠   ∗ 0 G ≠ ∗ 0 𝐺 𝑖 G i ∗ 𝑛 𝑖 ∗ n i ∗ 𝑛 𝑖 ∗ n i 𝐺 𝑖 G i 𝐺 𝑖   +   ∗ 𝑛 𝑖 G i + ∗ n i 𝐺 𝑖   ≈   ∗ 𝑛 𝑖 G i ≈ ∗ n i 𝐺   ≈ 𝐺 ′ G ≈ G ′ 
 第二步,需要说明 𝐺 ′   ≈   ∗ 𝑚 G ′ ≈ ∗ m 引理 2 ,只需要证明 𝐺 ′   +   ∗ 𝑚 G ′ + ∗ m 𝐺 ′   ≠   ∗ 0 G ′ ≠ ∗ 0 ∗ 𝑛 𝑖   ∈   ∗ 𝑚 ∗ n i ∈ ∗ m 𝑚 m ∗ 𝑛 𝑖   ∈ 𝐺 ′ ∗ n i ∈ G ′ ∗ 𝑛 𝑖   +   ∗ 𝑛 𝑖   ∈ P ∗ n i + ∗ n i ∈ P ∗ 𝑛 𝑖   ∈ 𝐺 ∗ n i ∈ G 𝑛 𝑖   < 𝑚 n i < m ∗ 𝑛 𝑖   ∈   ∗ 𝑚 ∗ n i ∈ ∗ m ∗ 𝑛 𝑖   +   ∗ 𝑛 𝑖   ∈ P ∗ n i + ∗ n i ∈ P ∗ 𝑛 𝑖   ∈ 𝐺 ∗ n i ∈ G 𝑛 𝑖   > 𝑚 n i > m ∗ 𝑚   ∈   ∗ 𝑛 𝑖 ∗ m ∈ ∗ n i ∗ 𝑚   +   ∗ 𝑚   ∈ P ∗ m + ∗ m ∈ P 𝐺 ′   ≈   ∗ 𝑚 G ′ ≈ ∗ m 
 由等价关系的传递性可知,𝐺   ≈   ∗ 𝑚 G ≈ ∗ m 𝐺 G 
这一结论说明,可以为每一个公平游戏 𝐺 G 𝑛 n 𝐺   ≈   ∗ 𝑛 G ≈ ∗ n 
Nim 数  一个公平游戏 𝐺 G Nim 数 (nimber)就是使得 𝐺   ≈   ∗ 𝑛 G ≈ ∗ n 𝑛 n 
这个将公平游戏映射到 Nim 数的函数称为 Sprague–Grundy 函数 (Sprague–Grundy function),简称 SG 函数 ,记作 S G  (   ⋅ ) SG  ( ⋅ ) 
根据本节定理的证明过程可知,Sprague–Grundy 函数可以递归地计算如下:
推论  公平游戏 𝐺 G 𝑥 x S G  ( 𝑥 ) SG  ( x ) 
 S G  ( 𝑥 ) = m e x  { S G  ( 𝑥 ′ ) : 𝑥 ′ ∈ 𝑥 } . SG  ( x ) = mex  { SG  ( x ′ ) : x ′ ∈ x } . 其中,m e x  ( 𝐴 )   : = m i n { 𝑛   ∈ 𝐍   : 𝑛   ∉ 𝐴 } mex  ( A ) := min { n ∈ N : n ∉ A } 𝐴 A 
也就是说,一个状态的 SG 函数值,等于它的所有后继状态的 SG 函数值的 m e x mex 
利用 SG 函数值(即 Nim 数),可以判断一个状态是否为先手必胜状态。
推论  公平游戏 𝐺 G 𝑥 x S G  ( 𝑥 )   ≠ 0 SG  ( x ) ≠ 0 
最后,游戏的和的 SG 函数值,就是子游戏的 SG 函数值的 Nim 和(即异或)。
定理(Sprague–Grundy)  对于公平游戏 𝐺 1 , 𝐺 2 , ⋯ , 𝐺 𝑛 G 1 , G 2 , ⋯ , G n 
 S G  ( 𝐺 1 + 𝐺 2 + ⋯ + 𝐺 𝑛 ) = S G  ( 𝐺 1 ) ⊕ S G  ( 𝐺 2 ) ⊕ ⋯ ⊕ S G  ( 𝐺 𝑛 ) . SG  ( G 1 + G 2 + ⋯ + G n ) = SG  ( G 1 ) ⊕ SG  ( G 2 ) ⊕ ⋯ ⊕ SG  ( G n ) . 证明  因为 ∗ 𝑎 1   +   ∗ 𝑎 2   + ⋯   +   ∗ 𝑎 𝑛 ∗ a 1 + ∗ a 2 + ⋯ + ∗ a n ( 𝑎 1 , 𝑎 2 , ⋯ , 𝑎 𝑛 ) ( a 1 , a 2 , ⋯ , a n ) 
 ∗ 𝑎 1 + ∗ 𝑎 2 + ⋯ + ∗ 𝑎 𝑛 + ∗ ( 𝑎 1 ⊕ 𝑎 2 ⊕ ⋯ ⊕ 𝑎 𝑛 ) ∗ a 1 + ∗ a 2 + ⋯ + ∗ a n + ∗ ( a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n ) 是先手必败的。根据 引理 2 ,有
 ∗ 𝑎 1 + ∗ 𝑎 2 + ⋯ + ∗ 𝑎 𝑛 ≈ ∗ ( 𝑎 1 ⊕ 𝑎 2 ⊕ ⋯ ⊕ 𝑎 𝑛 ) . ∗ a 1 + ∗ a 2 + ⋯ + ∗ a n ≈ ∗ ( a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n ) . 所以,有
 S G  ( ∗ 𝑎 1 + ∗ 𝑎 2 + ⋯ + ∗ 𝑎 𝑛 ) = 𝑎 1 ⊕ 𝑎 2 ⊕ ⋯ ⊕ 𝑎 𝑛 . SG  ( ∗ a 1 + ∗ a 2 + ⋯ + ∗ a n ) = a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n . 设 𝑎 𝑖   = S G  ( 𝐺 𝑖 ) a i = SG  ( G i ) 𝐺 𝑖   ≈   ∗ 𝑎 𝑖 G i ≈ ∗ a i ≈ ≈ 
 ( 𝐺 1 + 𝐺 2 + ⋯ + 𝐺 𝑛 ) + ( ∗ 𝑎 1 + ∗ 𝑎 2 + ⋯ + ∗ 𝑎 𝑛 ) = 𝑛 ∑ 𝑖 = 1 ( 𝐺 𝑖 + ∗ 𝑎 𝑖 ) ∈ P . ( G 1 + G 2 + ⋯ + G n ) + ( ∗ a 1 + ∗ a 2 + ⋯ + ∗ a n ) = ∑ i = 1 n ( G i + ∗ a i ) ∈ P . 所以,就有
 S G  ( 𝐺 1 + 𝐺 2 + ⋯ + 𝐺 𝑛 ) = S G  ( ∗ 𝑎 1 + ∗ 𝑎 2 + ⋯ + ∗ 𝑎 𝑛 ) = 𝑎 1 ⊕ 𝑎 2 ⊕ ⋯ ⊕ 𝑎 𝑛 = S G  ( 𝐺 1 ) ⊕ S G  ( 𝐺 2 ) ⊕ ⋯ ⊕ S G  ( 𝐺 𝑛 ) . SG  ( G 1 + G 2 + ⋯ + G n ) = SG  ( ∗ a 1 + ∗ a 2 + ⋯ + ∗ a n ) = a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n = SG  ( G 1 ) ⊕ SG  ( G 2 ) ⊕ ⋯ ⊕ SG  ( G n ) . 利用这一定理,在计算游戏的和的 SG 函数值时,可以大幅简化计算。
由此,可以总结出 SG 函数值的计算方法:
对于多个独立的游戏,可以分别计算它们的 SG 函数值,再求 Nim 和; 对于单个游戏,每个状态的 SG 函数值都是它的所有后继状态的 SG 函数值的 m e x mex  特别地,终止状态(即没有后继状态的状态)的 SG 函数值为 m e x  ∅   = 0 mex  ∅ = 0  Nim 数 所有的公平游戏都唯一对应一个 Nim 数。(有限)Nim 数的集合就是自然数集 𝐍 N ⊕ ⊕ ⊗ ⊗ 
Nim 数的运算  对于 Nim 数 𝑎 , 𝑏 a , b 
 Nim 和 𝑎   ⊕ 𝑏   = m e x  ( { 𝑎 ′   ⊕ 𝑏   : 𝑎 ′   < 𝑎 ,   𝑎 ′   ∈ 𝐍 }   ∪ { 𝑎   ⊕ 𝑏 ′   : 𝑏 ′   < 𝑏 ,   𝑏 ′   ∈ 𝐍 } ) a ⊕ b = mex  ( { a ′ ⊕ b : a ′ < a ,   a ′ ∈ N } ∪ { a ⊕ b ′ : b ′ < b ,   b ′ ∈ N } )  Nim 积 𝑎   ⊗ 𝑏   = m e x  ( { ( 𝑎 ′   ⊗ 𝑏 )   ⊕ ( 𝑎   ⊗ 𝑏 ′ )   ⊕ ( 𝑎 ′   ⊗ 𝑏 ′ )   : 𝑎 ′   < 𝑎 ,   𝑏 ′   < 𝑏 ,   𝑎 ′ , 𝑏 ′   ∈ 𝐍 } ) a ⊗ b = mex  ( { ( a ′ ⊗ b ) ⊕ ( a ⊗ b ′ ) ⊕ ( a ′ ⊗ b ′ ) : a ′ < a ,   b ′ < b ,   a ′ , b ′ ∈ N } )  全体 Nim 数在运算 ⊕ ⊕ ⊗ ⊗ 2 2 域 。而且,这些运算以及它们的逆运算,对于前 2 2 𝑛 2 2 n 2 2 𝑛 2 2 n 有限域  𝐅 2 2 𝑛 F 2 2 n 
常见的公平游戏 尽管 Sprague–Grundy 理论完全解决了公平游戏的问题,但是,处理实际的公平游戏时,直接应用 Sprague–Grundy 定理计算效率仍然不高。比如,Nim 游戏中,暴力计算 Sprague–Grundy 值的复杂度是指数级的。因此,往往需要通过打表的方式猜测具体的公平游戏的结论。
本节列举了一些常见的公平游戏及其结论。叙述结论时,本节只给出了必胜和必败状态的判断法则。至于必胜策略,就是进行恰当的操作,使得留给对手的局面恰好为必败状态。由于算法竞赛中经常出现这些游戏的变体,所以,掌握每个游戏的结论的证明过程也很重要。
本节结论的证明方法  本节结论的证明都是验证性的。对于一个游戏,结论中会描述它的先手必败状态和先手必胜状态。证明中,只需要验证从一个先手必败状态出发,只能得到先手必胜状态;而从先手必胜状态出发,总能得到至少一个先手必败状态。要将这些证明改写为严格的证明,需要建立博弈图,然后对博弈图上的状态应用数学归纳法,而这些验证的步骤就是其中的归纳部分。
Bachet 游戏 相较于单堆 Nim 游戏,Bachet 游戏限制了每次可以取走的石子的数量。
Bachet 游戏  有一堆石子,共计 𝑛 n 1 1 𝑘 k 
对此,有如下结论:
定理  游戏先手必败,当且仅当 𝑛   ≡ 0 ( m o d 𝑘   + 1 ) n ≡ 0 ( mod k + 1 ) 
证明一  当 𝑛   ≢ 0 ( m o d 𝑘   + 1 ) n ≢ 0 ( mod k + 1 ) 𝑛 m o d ( 𝑘 + 1 )   ∈ [ 1 , 𝑘 ] n mod ( k + 1 ) ∈ [ 1 , k ] 
 反过来,当 𝑛   ≡ 0 ( m o d 𝑘   + 1 ) n ≡ 0 ( mod k + 1 ) 𝑘 ′ k ′ 𝑘   + 1   − 𝑘 ′ k + 1 − k ′ 
证明二  作为 Sprague–Grundy 定理的应用,可以计算 𝑓 ( 𝑛 ) f ( n ) 𝑛 n 
 对于 𝑛   ≤ 𝑘 n ≤ k 𝑓 ( 𝑛 )   = 𝑛 f ( n ) = n 𝑛   > 𝑘 n > k 𝑓 ( 𝑛 )   = 𝑛 m o d ( 𝑘 + 1 ) f ( n ) = n mod ( k + 1 ) 
 𝑓 ( 𝑛 ) = m e x  { 𝑓 ( 𝑛 − 𝑘 ) , 𝑓 ( 𝑛 − 𝑘 + 1 ) , ⋯ , 𝑓 ( 𝑛 − 1 ) } . f ( n ) = mex  { f ( n − k ) , f ( n − k + 1 ) , ⋯ , f ( n − 1 ) } . 这遍历了模 𝑘   + 1 k + 1 𝑛 m o d ( 𝑘 + 1 ) n mod ( k + 1 ) 𝑓 ( 𝑛 )   = 𝑛 m o d ( 𝑘 + 1 ) f ( n ) = n mod ( k + 1 ) 
Moore's Nim-k 游戏 相较于 Nim 游戏,Moore's Nim-𝑘 k 𝑘 k 
Moore's Nim-𝑘 k   共有 𝑛 n 𝑖 i 𝑎 𝑖 a i 1 1 𝑘 k 
对此,有如下结论:
定理  将每一堆石子的数目都表示为二进制数,并对每个数位 𝑑 d 𝑑 d 1 1 ( 𝑘   + 1 ) ( k + 1 ) 0 0 
证明  仿照 Nim 游戏的结论的证明,很容易证明本结论。设 𝑑 d 0 0 𝑘 ′   ≤ 𝑘 k ′ ≤ k 𝑑 d 1 1 𝑘 k 0 0 
 实际上,只要选定 𝑘 ′ k ′ 2 𝑑 2 d 𝑑 d 0 0 
阶梯 Nim 游戏 阶梯 Nim 游戏稍微复杂一些,它允许石子在相邻的堆之间移动。
阶梯 Nim 游戏  共有 𝑛 n 𝑖 i 𝑎 𝑖 a i 1 1 𝑖   > 1 i > 1 𝑖   − 1 i − 1 
对此,有如下结论:
定理  游戏先手必败,当且仅当奇数堆石子数量的 Nim 和 𝑎 1   ⊕ 𝑎 3   ⊕ ⋯   ⊕ 𝑎 𝑛 − 1 + ( 𝑛 m o d 2 )   = 0 a 1 ⊕ a 3 ⊕ ⋯ ⊕ a n − 1 + ( n mod 2 ) = 0 
证明  任何玩家将偶数堆的石子移动到奇数堆时,对手都可以将这些石子继续移动到下一个偶数堆(或移走),因此,这样的移动不会影响奇数堆的局面。此时,每一个奇数堆向下移动到相邻的偶数堆(或移走)都可以看作独立的单堆 Nim 游戏。根据 Sprague–Grundy 定理关于游戏的和的结论,阶梯 Nim 游戏的 SG 函数值,是这些子游戏的 SG 函数值的 Nim 和。这就得到上述结论。
Fibonacci Nim 游戏 Fibonacci Nim 游戏类似 Bachet 游戏,只有一堆石子,且限制了每次取走的数量。与 Bachet 游戏不同,Fibonacci Nim 游戏中,每次取走的数量的限制是动态的。
Fibonacci Nim 游戏  有一堆石子,共计 𝑛 n 0 0 
对此,有如下结论:
定理  游戏开始时,先手必败,当且仅当石子数目 𝑛 n Fibonacci 数 。
证明  设 𝑞 q 𝑞   = 𝑛   − 1 q = n − 1 𝑞 q 𝑛 n Fibonacci 编码 ,也就是将 𝑛 n 𝑞 q 𝑛 n 
 必胜策略是:如果可以,移走所有剩余石子;否则,移走分解中最小的 Fibonacci 数。由于分解中,次小的 Fibonacci 数一定严格大于最小的 Fibonacci 数的两倍,所以,只要处于必胜状态的当前回合取不走所有石子,对手在下一回合也取不走次小的 Fibonacci 数(也就是下一回合最小的 Fibonacci 数),对手一定处于必败状态。
 反过来,如果当前处于必败状态,那么,设当前取走的数目为 𝑘 k 𝐹 F 𝐹 ′ F ′ 𝐹   − 𝑘 F − k 𝐹 ′   = 𝐹 ″   + 𝐹 ‴ F ′ = F ″ + F ‴ 𝐹 ″   > 𝐹 ‴ F ″ > F ‴ 𝐹 ‴ , 𝐹 ″ , 𝐹 ′ F ‴ , F ″ , F ′ 𝑘   < 𝐹 ″ k < F ″ 𝑘   + ( 𝐹   − 𝑘 ) k + ( F − k ) 𝐹 F 𝑘   ≥ 𝐹 ″ k ≥ F ″ 2 𝑘   > 𝐹 ″   + 𝐹 ‴   = 𝐹 ′ 2 k > F ″ + F ‴ = F ′ 
Wythoff 游戏 Wythoff 游戏允许同时从多堆石子中移除,但是要求每堆移除相同数量的石子。
Wythoff 游戏  有两堆石子,分别有 𝑎 1 a 1 𝑎 2 a 2 
对此,有如下结论:
定理  不妨设 𝑎 1   ≤ 𝑎 2 a 1 ≤ a 2 𝑎 1   = ⌊ ( 𝑎 2   − 𝑎 1 ) 𝜙 ⌋ a 1 = ⌊ ( a 2 − a 1 ) ϕ ⌋ 𝜙   = ( √ 5   + 1 ) / 2 ϕ = ( 5 + 1 ) / 2 
为了证明这一结论,需要用到如下引理:
Beatty 序列  设 𝑟   > 1 r > 1 B 𝑟   = { ⌊ 𝑘 𝑟 ⌋   : 𝑘   ∈ 𝐍 + } B r = { ⌊ k r ⌋ : k ∈ N + } 
Rayleigh 定理  设 𝑟 , 𝑠   > 1 r , s > 1 1 𝑟   + 1 𝑠   = 1 1 r + 1 s = 1 B 𝑟 B r B 𝑠 B s 𝐍 + N + 
证明  设 A 𝑟   = { 𝑘 𝑟   : 𝑘   ∈ 𝐍 + } A r = { k r : k ∈ N + } A   = A 𝑟   ∪ A ℓ A = A r ∪ A ℓ { 𝑎 𝑖 } 𝑖 ∈ 𝐍 + { a i } i ∈ N + 𝑖   = ⌊ 𝑎 𝑖 ⌋ i = ⌊ a i ⌋ 𝑖   ∈ 𝐍 + i ∈ N + B 𝑟   ∪ B 𝑠 B r ∪ B s 𝐍 + N + 
 首先,证明序列里没有重复的元素。假设不然,存在 𝑘 , ℓ   ∈ 𝐍 + k , ℓ ∈ N + 𝑘 𝑟   = ℓ 𝑠 k r = ℓ s 
 ℓ 𝑘 = 𝑟 𝑠 = 𝑟 − 1 . ℓ k = r s = r − 1. 但是,等式左侧是有理数,等式右侧是无理数,矛盾。因此,序列的数字各不相同。
 然后,证明集合 A A 𝑎 𝑖 a i ⌊ 𝑎 𝑖 ⌋ ⌊ a i ⌋ 𝑎 𝑖   ∈ A 𝑟 a i ∈ A r 𝑎 𝑖   = 𝑘 𝑟 a i = k r A 𝑟 A r A ℓ A ℓ 𝑎 𝑖 a i 
 𝑘 + ⌊ 𝑘 𝑟 𝑠 ⌋ = 𝑘 + ⌊ 𝑘 ( 𝑟 − 1 ) ⌋ = ⌊ 𝑘 𝑟 ⌋ = ⌊ 𝑎 𝑖 ⌋ k + ⌊ k r s ⌋ = k + ⌊ k ( r − 1 ) ⌋ = ⌊ k r ⌋ = ⌊ a i ⌋ 个。进而,由于序列 { 𝑎 𝑖 } { a i } 𝑎 𝑖 a i 𝑖 i 𝑖   = ⌊ 𝑎 𝑖 ⌋ i = ⌊ a i ⌋ 
由此,可以得到前述结论的证明。
Wythoff 游戏结论的证明  对于所有 𝑎 1   < 𝑎 2 a 1 < a 2 ( 𝑎 1 , 𝑎 2 ) ( a 1 , a 2 ) 𝑘   = 𝑎 2   − 𝑎 1   ∈ 𝐍 + k = a 2 − a 1 ∈ N + 𝑎 1   = ⌊ 𝑘 𝜙 ⌋ a 1 = ⌊ k ϕ ⌋ 𝑎 2   = ⌊ 𝑘 ( 𝜙   + 1 ) ⌋ a 2 = ⌊ k ( ϕ + 1 ) ⌋ 𝜙 ϕ 1 𝜙   + 1 𝜙 + 1   = 1 1 ϕ + 1 ϕ + 1 = 1 { ⌊ 𝑘 𝜙 ⌋ } { ⌊ k ϕ ⌋ } ⌊ 𝑘 ( 𝜙   + 1 ) ⌋ ⌊ k ( ϕ + 1 ) ⌋ 𝐍 + N + 𝑎 1   < 𝑎 2 a 1 < a 2 ( 𝑎 1 , 𝑎 2 ) ( a 1 , a 2 ) 𝑎 1 a 1 𝑎 2 a 2 𝑎 2   − 𝑎 1 a 2 − a 1 
 由于 Wythoff 游戏中,一次合法的操作要么保持分量之一不变,要么保持分量之差不变,所以,从一个先手必败状态开始,确实无法由一次合法的操作中得到另一个先手必败状态。反过来,对于任何先手必胜状态 ( 𝑎 1 , 𝑎 2 ) ( a 1 , a 2 ) 𝑎 1   ≤ 𝑎 2 a 1 ≤ a 2 𝑘   = 𝑎 2   − 𝑎 1 k = a 2 − a 1 𝑎 1   > ⌊ 𝑘 𝜙 ⌋ a 1 > ⌊ k ϕ ⌋ ( 𝑎 1   − ⌊ 𝑘 𝜙 ⌋ ) ( a 1 − ⌊ k ϕ ⌋ ) 𝑎 1 a 1 ( 𝑎 1 , 𝑎 ′ 2 ) ( a 1 , a 2 ′ ) 𝑎 1   > 𝑎 ′ 2 a 1 > a 2 ′ 𝑎 ′ 2   < 𝑎 2 a 2 ′ < a 2 𝑎 1   < 𝑎 ′ 2 a 1 < a 2 ′ 𝑘 ′   = 𝑎 ′ 2   − 𝑎 1 k ′ = a 2 ′ − a 1 𝑎 1   = ⌊ 𝑘 ′ 𝜙 ⌋ a 1 = ⌊ k ′ ϕ ⌋ 𝑎 1   < ⌊ 𝑘 𝜙 ⌋ a 1 < ⌊ k ϕ ⌋ 𝑘 ′   < 𝑘 k ′ < k 𝑎 ′ 2   = 𝑎 1   + 𝑘 ′   < 𝑎 1   + 𝑘   = 𝑎 2 a 2 ′ = a 1 + k ′ < a 1 + k = a 2 𝑎 1   < ⌊ 𝑘 𝜙 ⌋ a 1 < ⌊ k ϕ ⌋ 𝑎 ′ 2   < 𝑎 2 a 2 ′ < a 2 ( 𝑎 2   − 𝑎 ′ 2 ) ( a 2 − a 2 ′ ) 
翻硬币游戏 翻硬币游戏也是一类常见的公平组合游戏。
翻硬币游戏  设 ( 𝑆 ,   ⪯ ) ( S , ⪯ ) 良基偏序集 ,映射 𝑓   : 𝑆   → P P 𝑆 f : S → P P S 𝑠   ∈ 𝑆 s ∈ S 𝑓 ( 𝑠 ) f ( s ) 𝑇   ∈ 𝑓 ( 𝑠 ) T ∈ f ( s ) 𝑠   ∈ 𝑇 s ∈ T 𝑡   ∈ 𝑇 t ∈ T 𝑡   ⪯ 𝑠 t ⪯ s 𝑆 S 𝑠 s 𝑇   ∈ 𝑓 ( 𝑠 ) T ∈ f ( s ) 𝑇 T 
翻硬币游戏其实是一大类游戏。取决于具体的偏序集 𝑆 S 𝑓 f 𝑓 f 𝑇 T 𝑠 s 𝑇 T 𝑠 s 
例子  设 𝑆   = { 1 , 2 , ⋯ , 𝑛 } S = { 1 , 2 , ⋯ , n } 𝑓 ( 𝑠 )   = { { 𝑡 , 𝑠 }   : 𝑡   ≤ 𝑠 } f ( s ) = { { t , s } : t ≤ s } 𝑛 n  设 𝑆   = { 1 , 2 , ⋯ , 𝑛 } S = { 1 , 2 , ⋯ , n } 𝑓 ( 𝑠 )   = { [ 𝑡 , 𝑠 ]   : 𝑡   ≤ 𝑠 } f ( s ) = { [ t , s ] : t ≤ s } 𝑛 n  设 𝑆   = { 1 , 2 , ⋯ , 𝑛 } 2 S = { 1 , 2 , ⋯ , n } 2 𝑓 ( 𝑠 )   = { { 𝑠 } } f ( s ) = { { s } } 𝑛 n 𝑛 n  设 𝑆 S 𝑓 ( 𝑠 ) f ( s ) 𝑠 s 𝑠 s  尽管翻硬币游戏种类繁多,但是它们的求解思路是一致的。对于翻硬币游戏 ( 𝑆 , 𝑓 ) ( S , f ) 𝐺 𝑠 G s 𝑠 s 𝐺 G 
定理  对于翻硬币游戏 ( 𝑆 , 𝑓 ) ( S , f ) 𝐺 G 𝐻 ( 𝐺 )   ⊆ 𝑆 H ( G ) ⊆ S 𝐺 G 
 S G  ( 𝐺 ) = ⨁ 𝑠 ∈ 𝐻 ( 𝐺 ) S G  ( 𝐺 𝑠 ) . SG  ( G ) = ⨁ s ∈ H ( G ) SG  ( G s ) . 证明  考虑一个相关的游戏:一个局面 𝐺 ′ G ′ 𝑆 S 𝑠 s 𝑇   ∈ 𝑓 ( 𝑠 ) T ∈ f ( s ) 𝑇   ∖ { 𝑠 } T ∖ { s } 𝐺 ′ 𝑠 G s ′ 𝑠 s 𝐺 ′ G ′ 𝐺 ′ G ′ 𝐻 ( 𝐺 ′ ) H ( G ′ ) 
 S G  ( 𝐺 ′ ) = ⨁ 𝑠 ∈ 𝐻 ( 𝐺 ′ ) S G  ( 𝐺 ′ 𝑠 ) . SG  ( G ′ ) = ⨁ s ∈ H ( G ′ ) SG  ( G s ′ ) . 由此,下文只需要建立游戏 𝐺 ′ G ′ 𝐺 G 
 需要说明的是,对于新游戏的局面 𝐺 ′ G ′ 𝐺 G 𝐺 ′ G ′ 𝐺 G 𝐺 ′ G ′ 𝐺 G Sprague–Grundy 定理的引理 2 ,这等价于证明局面 𝐺   + 𝐺 ′ G + G ′ 𝑠 s 𝑠 s 𝑇   ∈ 𝑓 ( 𝑠 ) T ∈ f ( s ) 
利用这一结论,判断某一局面是否必胜,只需要计算其中所有正面朝上的硬币对应的基础局面的 SG 函数值,再求 Nim 和即可。这些基础局面的 SG 函数值也不难计算,因为它们的后继局面已经由映射 𝑓 f 
S G  ( 𝐺 𝑠 ) = m e x 𝑇 ∈ 𝑓 ( 𝑠 )  ⨁ 𝑡 ∈ 𝑇 ∖ { 𝑠 } S G  ( 𝐺 𝑡 ) . SG  ( G s ) = mex T ∈ f ( s )  ⨁ t ∈ T ∖ { s } SG  ( G t ) . 这相当于提供了一个基础局面 SG 函数值的递推公式。
二分图博弈 前置知识:二分图最大匹配 
本节的最后,讨论二分图博弈。尽管这个游戏常称作二分图博弈,但是它的描述和结论的证明都与二分图的结构无关,所以,它的结论实际上对于一般的无向图都成立。但是,一般图的最大匹配较为复杂,所以这一结论常出现在二分图的题目中。
二分图博弈  两个玩家轮流行动。每个玩家面临的局面都由一个无向图 𝐺   = ( 𝑉 , 𝐸 ) G = ( V , E ) 𝑣   ∈ 𝑉 v ∈ V ( 𝐺 , 𝑣 ) ( G , v ) 𝑣 v 𝑢 u 𝑣 v 𝐺 G 𝐺 ′ G ′ ( 𝐺 ′ , 𝑢 ) ( G ′ , u ) 𝑣 v 
对此,有如下结论:
定理  游戏先手必胜,当且仅当顶点 𝑣 v 𝐺 G 𝐺 G 𝑣 v 
证明  首先,顶点 𝑣 v 𝐺 G 𝐺 G 𝑀 M 𝑀 M 𝑣 v 𝑢 u 𝑣 v 𝐺 G 𝐺 ′ G ′ | 𝑀 |   − 1 | M | − 1 𝑀 M ( 𝑣 , 𝑣 ) ( v , v ) 𝐺 ′ G ′ | 𝑀 |   − 1 | M | − 1 𝑀 ′ M ′ 𝑀 ′ M ′ 𝐺 ′ G ′ 𝑢 u 𝑀 ′ M ′ 
 反过来,假设存在最大匹配 𝑀 M 𝑣 v 𝑀 M 𝑣 v 𝑀 M 
求出二分图最大匹配关键点的算法详见 二分图最大匹配页面 。
另外,二分图博弈还有一个变体:
二分图博弈的变体  设 𝐺   = ( 𝑉 , 𝐸 ) G = ( V , E ) 
显然,这个变体相当于在前文所述二分图博弈中,让先手玩家选择初始局面,然后从后手玩家开始二分图博弈。因此,这个变体中,先手玩家必败,当且仅当每个顶点都是最大匹配关键点,亦即图 𝐺 G 完美匹配 。
反常 Nim 游戏 本节讨论反常 Nim 游戏的求解。
Nim 游戏  共有 𝑛 n 𝑖 i 𝑎 𝑖 a i 
对此,有如下结论:
定理  反常 Nim 游戏中,状态 ( 𝑎 1 , 𝑎 2 , ⋯ , 𝑎 𝑛 ) ( a 1 , a 2 , ⋯ , a n ) P P 
 存在 𝑖 i 𝑎 𝑖   > 1 a i > 1 𝑎 1   ⊕ 𝑎 2   ⊕ ⋯   ⊕ 𝑎 𝑛   = 0 a 1 ⊕ a 2 ⊕ ⋯ ⊕ a n = 0  对于所有 𝑖 i 𝑎 𝑖   ≤ 1 a i ≤ 1  证明  由于无法操作是先手必胜态 N N N N N N 
 接下来,考察有些堆石子的数量严格大于 1 1 
 情形 A:如果只有一堆石子的数量严格大于 1 1 0 0 1 1 N N 
 情形 B:现在,有不止一堆石子的数量严格大于 1 1 1 1 
有向图游戏 本文讨论的公平组合游戏,要求同一局面不能出现两次,也不存在平局的可能性。因此,对应的博弈图总是有向无环图。本节放宽了这一限制,讨论如何在一般的有向图上判定各个状态是先手必胜、先手必败或平局。
有向图游戏的规则和其他的公平组合游戏大体一致:从起始状态出发,轮流沿着有向图的边移动一步,直到无路可走。根据游戏是正常规则还是反常规则,最后一个不能移动的玩家分别是败者和胜者。在这样的游戏里,每个状态的胜负情况共有三种可能性:先手必胜、先手必败、平局。平局中游戏永远不会终止。尽管稍微复杂一些,但是关于必败状态和必胜状态的 引理  依然成立,而剩下的状态就是平局状态:
一个状态有后继状态先手必胜,当且仅当后继状态之一是必败状态; 如果一个状态有后继状态,那么它先手必败,当且仅当所有后继状态都是必胜状态; 如果一个状态无法分类为必胜状态和必败状态,那么它就是平局状态。 要将所有状态分类为这三种状态,只需要采用类似 拓扑排序  的思路:
初始化时,记录所有状态的出度,将所有出度为零的状态压入队列,并根据游戏是正常规则或是反常规则分别设为必败状态或必胜状态。 弹出队首状态。如果是必败状态,则设前驱状态为必胜状态;否则,当前状态是必胜状态,将它的所有前驱状态的出度减一,并将出度为零的前驱状态设为必败状态。将可以判断是必胜或必败状态的前驱状态压入队列。 算法在队列为空时终止。尚未判断为必胜或必败状态的状态均为平局状态。 这一算法可以在 𝑂 ( | 𝑉 |   + | 𝐸 | ) O ( | V | + | E | ) 
例题 本节讨论一些典型的例题。
Luogu P2148 [SDOI2009] E&D 有 2 𝑛 2 n 𝑘   = 1 , 2 , ⋯ , 𝑛 k = 1 , 2 , ⋯ , n 2 𝑘   − 1 2 k − 1 2 𝑘 2 k { 𝑎 𝑖 } 2 𝑛 𝑖 = 1 { a i } i = 1 2 n 
解答  显然,不同组石子堆的游戏相互独立,所以,只要计算每组游戏的 SG 函数值,就能计算出整个游戏的 SG 值,进而判断是否为必胜状态。关键在于如何计算每组石子堆的 SG 函数值。这并不容易。解决这类博弈论问题的常见思路是打表。设一组石子堆中石子数量分别为 ( 𝑖 , 𝑗 ) ( i , j ) 𝑓 ( 𝑖 , 𝑗 ) f ( i , j ) 
  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 0 1 0 2 0 1 0 3 0 1 0 2 0 1 0 4 
1 1 2 2 1 1 3 3 1 1 2 2 1 1 4 4
0 2 0 2 0 3 0 3 0 2 0 2 0 4 0 4
2 2 2 2 3 3 3 3 2 2 2 2 4 4 4 4
0 1 0 3 0 1 0 3 0 1 0 4 0 1 0 4
1 1 3 3 1 1 3 3 1 1 4 4 1 1 4 4
0 3 0 3 0 3 0 3 0 4 0 4 0 4 0 4
3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4
0 1 0 2 0 1 0 4 0 1 0 2 0 1 0 4
1 1 2 2 1 1 4 4 1 1 2 2 1 1 4 4
0 2 0 2 0 4 0 4 0 2 0 2 0 4 0 4
2 2 2 2 4 4 4 4 2 2 2 2 4 4 4 4
0 1 0 4 0 1 0 4 0 1 0 4 0 1 0 4
1 1 4 4 1 1 4 4 1 1 4 4 1 1 4 4
0 4 0 4 0 4 0 4 0 4 0 4 0 4 0 4
4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
这个表很具有规律性。有一个简单的观察:表格分成若干个 2   × 2 2 × 2 0 0 2   × 2 2 × 2 
 1 2 1 3 1 2 1 4 
2 2 3 3 2 2 4 4 
1 3 1 3 1 4 1 4 
3 3 3 3 4 4 4 4 
1 2 1 4 1 2 1 4 
2 2 4 4 2 2 4 4 
1 4 1 4 1 4 1 4 
4 4 4 4 4 4 4 4 
可以发现,这个压缩的表格是前面完整表格相同位置的值加一。其实问题已经解决了。设下标从 0 0 ( 𝑖 , 𝑗 ) ( i , j ) 𝑔 ( 𝑖 , 𝑗 ) g ( i , j ) 
 𝑔 ( 𝑖 , 𝑗 ) = { 0 , i f   2 ∣ 𝑖   a n d   2 ∣ 𝑗 , 𝑔 ( ⌊ 𝑖 / 2 ⌋ , ⌊ 𝑗 / 2 ⌋ ) + 1 , o t h e r w i s e . g ( i , j ) = { 0 , if  2 ∣ i  and  2 ∣ j , g ( ⌊ i / 2 ⌋ , ⌊ j / 2 ⌋ ) + 1 , otherwise . 要求的 SG 函数 𝑓 ( 𝑖 , 𝑗 )   = 𝑔 ( 𝑖   − 1 , 𝑗   − 1 ) f ( i , j ) = g ( i − 1 , j − 1 ) 𝑂 ( l o g  m i n { 𝑖 , 𝑗 } ) O ( log  min { i , j } ) 𝑓 ( 𝑖 , 𝑗 ) f ( i , j ) 
 当然,可以通过简单的归纳法得到 𝑔 ( 𝑖 , 𝑗 ) g ( i , j ) 𝑖 i 𝑗 j 2 2 𝑖 i 𝑗 j 1 1 __builtin_ctz(~(i | j)) 算出该值。
 这类题目中,只要通过打表观察的方法得到 SG 函数表达式,它都很容易通过归纳法证明,因而解题的关键在于以某种形式获得这些结论而非推导。例如,已知结论后,本题中的递推关系可以归纳证明如下。设 𝑆 𝑘 S k 𝑘 k 𝑓 ( 𝑖 , 𝑗 )   = m e x  ( 𝑆 𝑖   ∪ 𝑆 𝑗 ) f ( i , j ) = mex  ( S i ∪ S j ) 𝑆 𝑘 S k 
 𝑆 𝑘 = { m e x  ( 𝑆 𝑖 ∪ 𝑆 𝑗 ) : 𝑖 + 𝑗 = 𝑘 ,   𝑖 , 𝑗 ∈ 𝐍 + } . S k = { mex  ( S i ∪ S j ) : i + j = k ,   i , j ∈ N + } . 需要证明的是,𝑑   ∈ 𝑆 𝑘 d ∈ S k ( 𝑘   − 1 ) ( k − 1 ) 𝑑 d 0 0 1 1 
 利用数学归纳法。归纳起点 𝑆 1   = ∅ S 1 = ∅ 𝑘 k 𝑑   ∈ 𝑆 𝑘 d ∈ S k 𝑖 , 𝑗   ∈ 𝐍 + i , j ∈ N + 𝑖   + 𝑗   = 𝑘 i + j = k ( 𝑖   − 1 ) ( i − 1 ) ( 𝑗   − 1 ) ( j − 1 ) 𝑑 ′   < 𝑑 d ′ < d 1 1 𝑑 d 0 0 0   ∼ 𝑑 0 ∼ d 2 𝑑 + 1 2 d + 1 ( 𝑘   − 1 )   = ( 𝑖   − 1 )   + ( 𝑗   − 1 )   + 1 ( k − 1 ) = ( i − 1 ) + ( j − 1 ) + 1 [ 2 𝑑 , 2 𝑑 + 1   − 1 ) [ 2 d , 2 d + 1 − 1 ) ( 𝑘   − 1 ) ( k − 1 ) 𝑑 d 1 1 
参考代码   1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 #include   <iostream> 
#include   <vector> 
int   g ( int   i ,   int   j )   { 
   return   i   %   2   ==   0   &&   j   %   2   ==   0   ?   0   :   g ( i   /   2 ,   j   /   2 )   +   1 ; 
} 
int   f ( int   i ,   int   j )   {   return   g ( i   -   1 ,   j   -   1 );   } 
int   main ()   { 
   int   t ; 
   std :: cin   >>   t ; 
   for   (;   t ;   -- t )   { 
     int   n ; 
     std :: cin   >>   n ; 
     int   v   =   0 ; 
     for   ( int   i   =   0 ;   i   <   n   /   2 ;   ++ i )   { 
       int   x ,   y ; 
       std :: cin   >>   x   >>   y ; 
       v   ^=   f ( x ,   y ); 
     } 
     std :: cout   <<   ( v   ?   "YES"   :   "NO" )   <<   '\n' ; 
   } 
   return   0 ; 
} 
Luogu P5675 [GZOI2017] 取石子游戏 有 𝑛 n 𝑖 i 𝑎 𝑖 a i 𝑛 , 𝑎 𝑖   ≤ 2 0 0 n , a i ≤ 200 
解答  对于这类问题,需要利用常见游戏的结论,并结合其他部分知识来进行解答。假设指定先手必须取走第 𝑖 i 𝑣 v 𝑎 𝑖   ≤ 𝑎 𝑖   ⊕ 𝑣 a i ≤ a i ⊕ v 𝑖 i 𝑎 𝑖 a i 𝑖 i 𝑎 𝑖   ⊕ 𝑣 a i ⊕ v 𝑖 i 𝑎 𝑖 a i 
参考代码   1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 #include   <array> 
#include   <iostream> 
#include   <vector> 
int   main ()   { 
   constexpr   int   M   =   1e9   +   7 ; 
   constexpr   int   L   =   256 ; 
   int   n ; 
   std :: cin   >>   n ; 
   std :: vector < int >   a ( n ); 
   for   ( auto &   x   :   a )   std :: cin   >>   x ; 
   int   res   =   0 ; 
   for   ( int   i   =   0 ;   i   <   n ;   ++ i )   { 
     std :: array < int ,   L >   dp   =   {}; 
     dp [ 0 ]   =   1 ; 
     for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
       if   ( j   ==   i )   continue ; 
       std :: array < int ,   L >   _dp   =   {}; 
       for   ( int   v   =   0 ;   v   <   L ;   ++ v )   { 
         _dp [ v ]   =   ( dp [ v ]   +   dp [ v   ^   a [ j ]])   %   M ; 
       } 
       dp   =   std :: move ( _dp ); 
     } 
     for   ( int   v   =   a [ i ];   v   <   L ;   ++ v )   { 
       res   =   ( res   +   dp [ v ])   %   M ; 
     } 
   } 
   std :: cout   <<   res   <<   std :: endl ; 
   return   0 ; 
} 
Luogu P2599 [ZJOI2009] 取石子游戏 有 𝑛 n 𝑖 i 𝑎 𝑖 a i 
解答  由于本题中并不存在相互独立的子游戏,所有这道题目原则上只用到 判断必败和必胜状态的引理 。从最简单的情形开始分析。当 𝑛   ≤ 2 n ≤ 2 𝑛   ≥ 3 n ≥ 3 𝑥 x 𝑦 y 𝑓 ( 𝑥 , 𝑦 ) f ( x , y ) 𝑓 ( 𝑥 , 𝑦 )   = 1 f ( x , y ) = 1 𝑓 ( 𝑥 , 𝑦 )   = 0 f ( x , y ) = 0 𝑓 ( 𝑥 , 𝑦 ) f ( x , y ) 𝑓 ( 𝑥 , 𝑦 )   = 0 f ( x , y ) = 0 𝑠   < 𝑥 s < x 𝑡   < 𝑦 t < y 𝑓 ( 𝑥 , 𝑡 )   = 𝑓 ( 𝑠 , 𝑦 )   = 1 f ( x , t ) = f ( s , y ) = 1 𝑥   = 0 x = 0 𝑦   = 0 y = 0 𝑛 n 𝑓 ( 𝑥 , 0 ) f ( x , 0 ) 𝑓 ( 0 , 𝑦 ) f ( 0 , y ) 𝑓 ( 𝑥 , 𝑦 ) f ( x , y ) 𝐍   × 𝐍 N × N 𝑓 ( 𝑥 , 𝑦 ) f ( x , y ) 0 0 1 1 0 0 0 0 0 0 0 0 𝑥 x 𝑦 y 𝑓 ( 𝑥 , 0 )   = 0 f ( x , 0 ) = 0 𝑥 x 𝑥 0 x 0 𝑓 ( 0 , 𝑦 )   = 0 f ( 0 , y ) = 0 𝑦 y 𝑦 0 y 0 𝑥 x 𝑓 ( 𝑥 , 𝑦 )   = 0 f ( x , y ) = 0 
 𝑦 = ⎧ { { { ⎨ { { { ⎩ 0 , 𝑥 = 𝑥 0 , 𝑥 − 1 , 𝑥 0 < 𝑥 < 𝑦 0 , 𝑥 + 1 , 𝑦 0 < 𝑥 < 𝑥 0 , 𝑥 , o t h e r w i s e . y = { 0 , x = x 0 , x − 1 , x 0 < x < y 0 , x + 1 , y 0 < x < x 0 , x , otherwise . 也就是说,只要知道 𝑥 0 x 0 𝑦 0 y 0 𝑂 ( 1 ) O ( 1 ) 𝑓 ( 𝑥 , 𝑦 ) f ( x , y ) 𝑥 0 x 0 𝑦 0 y 0 𝑥 0 x 0 𝑓 ( 𝑥 , 0 )   = 0 f ( x , 0 ) = 0 𝑓 ( 𝑥 , 0 ) f ( x , 0 ) 𝑛   − 1 n − 1 𝑛   − 1 n − 1 𝑓 1 , 𝑛 − 1 ( 𝑥 , 𝑦 ) f 1 , n − 1 ( x , y ) 𝑓 ( 𝑥 , 0 )   = 𝑓 1 , 𝑛 − 1 ( 𝑥 , 𝑎 𝑛 − 1 ) f ( x , 0 ) = f 1 , n − 1 ( x , a n − 1 ) 𝑓 2 , 𝑛 ( 𝑥 , 𝑦 ) f 2 , n ( x , y ) 𝑓 ( 0 , 𝑦 )   = 𝑓 2 , 𝑛 ( 𝑎 1 , 𝑦 ) f ( 0 , y ) = f 2 , n ( a 1 , y ) 𝑓 1 , 𝑛 − 1 ( 𝑥 , 𝑦 ) f 1 , n − 1 ( x , y ) 𝑓 2 , 𝑛 ( 𝑥 , 𝑦 ) f 2 , n ( x , y ) 区间 DP 。每层只需要维护相应函数的 𝑥 0 x 0 𝑦 0 y 0 
参考代码   1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 #include   <array> 
#include   <iostream> 
#include   <vector> 
int   main ()   { 
   // Transition helper as explained above. 
   auto   calc   =   []( int   x0 ,   int   y0 ,   int   x )   ->   int   { 
     return   x   ==   x0   ?   0 
                    :   (( x   <   x0   &&   x   <   y0 )   ||   ( x   >   x0   &&   x   >   y0 ) 
                           ?   x 
                           :   ( x0   <   y0   ?   x   -   1   :   x   +   1 )); 
   }; 
   int   t ; 
   std :: cin   >>   t ; 
   for   (;   t ;   -- t )   { 
     int   n ; 
     std :: cin   >>   n ; 
     std :: vector < int >   a ( n ); 
     for   ( auto &   x   :   a )   std :: cin   >>   x ; 
     // dp[i][j][0] = the unique value x such that 
     //     f_{i-1,j}(x, a_j) = 0 
     // i.e. the interval game [i, j] is in a P-position 
     // when a pile of size x is attached to the LEFT. 
     // 
     // dp[i][j][1] = the unique value y such that 
     //     f_{i,j+1}(a_i, y) = 0 
     // i.e. the interval game [i, j] is in a P-position 
     // when a pile of size y is attached to the RIGHT. 
     std :: vector < std :: vector < std :: array < int ,   2 >>>   dp ( 
         n ,   std :: vector < std :: array < int ,   2 >> ( n )); 
     // Base case: single-element interval [i, i]. 
     // The "left" and "right" pile values both equal a[i]. 
     for   ( int   i   =   0 ;   i   <   n ;   ++ i )   dp [ i ][ i ][ 0 ]   =   dp [ i ][ i ][ 1 ]   =   a [ i ]; 
     // Build up intervals of increasing length d. 
     for   ( int   d   =   1 ;   d   <   n ;   ++ d )   { 
       for   ( int   i   =   0 ;   i   +   d   <   n ;   ++ i )   { 
         dp [ i ][ i   +   d ][ 0 ]   = 
             calc ( dp [ i ][ i   +   d   -   1 ][ 1 ],   dp [ i ][ i   +   d   -   1 ][ 0 ],   a [ i   +   d ]); 
         dp [ i ][ i   +   d ][ 1 ]   =   calc ( dp [ i   +   1 ][ i   +   d ][ 0 ],   dp [ i   +   1 ][ i   +   d ][ 1 ],   a [ i ]); 
       } 
     } 
     // The original game corresponds to attaching nothing on the left. 
     // It is a P-position if and only if the unique value y satisfying 
     //     f_{-1, n-1}(0, y) = 0 
     // is y = 0. 
     std :: cout   <<   ( dp [ 0 ][ n   -   1 ][ 0 ]   !=   0 )   <<   '\n' ; 
   } 
   return   0 ; 
} 
习题 首先是一些模板题。它们是对本页面的结论的简单应用:
然后是一些思维性更强或更为综合的题目:
最后是一些二分图博弈的题目。由于需要用到一些二分图匹配的算法,故将它们单独列出:
参考资料与注释 2025/10/13 16:54:57 ,更新历史 在 GitHub 上编辑此页! Ir1d , c-forrest , Enter-tainer , StudyingFather , countercurrent-time , H-J-Granger , NachtgeistW , Backl1ght , CCXXXI , MegaOwIer , ouuan , SamZhangQingChuan , Tiphereth-A , woruo27 , AngelKitty , chu-yuehan , cjsoft , diauweb , Early0v0 , ezoixx130 , GekkaSaori , Konano , LovelyBuggies , Makkiy , Marcythm , mgt , minghu6 , P-Y-Y , PotassiumWings , sshwy , Suyun514 , tinjyu , weiyong1024 , 2008verser , billchenchina , ChungZH , cutekibry , cutekibry , FFjet , GavinZhengOI , Gesrua , isdanni , ksyx , kxccc , lychees , Molmin , orzAtalod , Peanut-Tang , SaMiiKaaaa , ShizuhaAki , SukkaW CC BY-SA 4.0  和 SATA