0
点赞
收藏
分享

微信扫一扫

【C++】【算法基础】Nim游戏

Go_Viola 2024-11-18 阅读 12

N i m Nim Nim游戏

给定 n n n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式

第一行包含整数 n n n

第二行包含 n n n 个数字,其中第 i i i 个数字表示第 i i i 堆石子的数量。

输出格式

如果先手方必胜,则输出 Yes

否则,输出 No

数据范围

1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105 1 ≤ 每堆石子数 ≤ 1 0 9 1≤每堆石子数≤10^9 1每堆石子数109

输入样例:
2
2 3
输出样例:
Yes

题解

N i m Nim Nim游戏是个挺古老的游戏,但是它的游戏解在 19 19 19世纪才被发现,所以如果没自己想出来也没关系的😏( d o g e . doge. doge.)​​

首先需了解一个位运算 X o r Xor Xor运算(异或运算),其数学符号为 ⊕ \oplus 。其中 a   X o r   b a\ Xor\ b a Xor b被定义为 a , b a,b a,b相异则为真, a , b a,b a,b相同则为假。它除了交换律,结合律外,还有一些神奇的性质:

  • **自身为自身的逆运算:**即对于任何两个布尔变量或者数有 ( a ⊕ b ) ⊕ b = a (a \oplus b)\oplus b=a (ab)b=a,可以依据这个写出swap(int a, int b)函数:a = a ^ b; b = a ^ b; a = a ^ b,(当然和本题没关系)。

  • **满足消去律:**即由 a ⊕ c = b ⊕ c a \oplus c=b \oplus c ac=bc可以推出 a = b a=b a=b

然后了解一下游戏中两个状态:

  • 必胜状态:先手进行某个操作时,留给了后手了一个必败状态,即先手可以走到某一个必败状态
  • 必败状态:先手无论如何操作,留给后手的都是必胜状态,即先手走不到任何一个必败状态

对于形如 N i m Nim Nim这类博弈问题最重要的是寻找必败态,对于这个必败态,其严格定义如下:

  • 无法进行任何移动的局面是必败态:如果一个玩家在当前状态下没有任何合法的移动可以做,那么这个状态就是必败态。
  • 可以移动到必败态的局面是非必败态:如果一个玩家能够通过一次合法的移动将游戏状态变为对手的必败态,则该玩家处于非必败态。
  • 在必败态做的所有操作的结果都是非必败态:处于必败态的一方,无论做出怎样的合法移动,都会导致自己仍然处于不利的状态。

结论

证明该结论的过程,即为证明其满足以上三个必败态的三条性质即可。

证明( B o u t o n ′ s T h e o r e m Bouton's Theorem BoutonsTheorem):

  • 对于第三个在必败态做的所有操作的结果都是非必败态,即为:对于某个局面 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus ...\oplus a_n=0 a1a2...an=0,一定不存在某个合法的移动,使得 a i a_i ai变为 a i ′ a_i' ai后, a 1 ⊕ a 2 ⊕ . . . ⊕ a i ′ ⊕ . . . ⊕ a n = x ≠ 0 a_1\oplus a_2\oplus...\oplus a_i'\oplus...\oplus a_n=x\neq 0 a1a2...ai...an=x=0

基于对三个必败态的证明:

  • 如果先手面对的局面是 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = x ≠ 0 a_1\oplus a_2\oplus...\oplus a_n=x\neq 0 a1a2...an=x=0,那么先手总可以拿走一堆若干个石子,将局面变为 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0 a1a2...an=0,如此重复,最后一定是后手面临最终没有石子可拿的状态,先手必胜。
  • 先手必败同理。
#include<iostream>
using namespace std;

int main()
{
    int n;
    cin >> n;
    int res = 0;
    while (n -- )
    {
        int x;
        cin >> x;
        res ^= x;
    }
    if(!res) cout << "No" << endl;
    else cout << "Yes" << endl;
    return 0;
}

如果将 N i m Nim Nim游戏的规则改变,如:

此时只知道异或运算暂时还无法应付该情况,所以还需了解一些其他道具。


SG函数

平等组合游戏 ( I C G ) (ICG) (ICG)一种两人轮流进行的、完全信息的、无运气因素的组合游戏,并且游戏中双方的可选行动和胜负条件完全对称,即对两位玩家没有偏向性。在这种游戏中,游戏的状态和操作规则都是公开的,双方都知道所有可能的走法,且没有隐藏信息或随机性。

  1. 两个人进行博弈

  2. 两人轮流进行决策且做出的决策都对自己有利

  3. 游戏中同一个局势不能多次到达且游戏不能有平局

  4. 当有人无法做出决策时游戏结束,无法做出决策的人输

  5. 游戏可以在有限步数内结束

  6. 任意游戏者在某一局势时能做出决策的集合只与当前局势有关,与游戏者本身无关

必胜态与必败态:定义 P − p o s i t i o n , N − p o s i t i o n P-position,N-position Pposition,Nposition

  • P − p o s i t i o n P−position Pposition:必败态(简称 P P P),即在决策前就处于这种状态的人必败
  • N − p o s i t i o n N−position Nposition:必胜态(简称 N N N),即在决策前就处于这种状态的人必胜

如果更严谨的定义 P P P N N N,则:

  • 无法移动的状态为 P P P,(或为 T e r m i n a l − p o s i t i o n Terminal-position Terminalposition

  • 可以移动到 P P P的状态为 N N N

  • 任意移动都会到 N N N的状态为 P P P

组合游戏中的 D A G DAG DAG​图:如果我们将每一种状态向它能到达的状态连边,那么一个组合游戏的所有状态就能组成一个 D A G DAG DAG(有向无环图):

给出一张DAG,在给定起点处有一枚棋子,两人轮流移动棋子,如果谁不能移动了就判输。

也就是说,任何一个 I C G ICG ICG都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。

S G SG SG函数是人们在研究博弈问题上迈出的一大步,人们不再一味的通过找规律来得到博弈结论,而是将博弈问题的每一个状态进行量化分析并总结出了某些普遍性的规律。

SG函数及SG定理

这个游戏可以认为是所有 I C G ICG ICG的抽象模型

下面我们在有向无环图的顶点定义** S p r a g u e − G a r u n d y Sprague-Garundy SpragueGarundy函数**。首先定义** m e x mex mex运算**,表示集合中最小的未出现的自然数,如: m e x { 1 , 2 , 3 } = 0 , m e x { 0 , 2 , 3 } = 1 mex\lbrace 1,2,3 \rbrace=0,mex\lbrace 0,2,3\rbrace=1 mex{1,2,3}=0,mex{0,2,3}=1

对于给定一个的有向无环图中,定义关于图的每个顶点的 S p r a g u e − G a r u n d y Sprague-Garundy SpragueGarundy函数为:
S G ( x ) = m e x { g ( y ) ∣ y 为 x 的后继 } = m e x ( { S G ( y 1 ) , S G ( y 2 ) , . . . , S G ( y k ) } ) SG(x)=mex\lbrace g(y)|y为x的后继\rbrace=mex(\lbrace SG(y_1),SG(y_2),...,SG(y_k)\rbrace) SG(x)=mex{g(y)yx的后继}=mex({SG(y1),SG(y2),...,SG(yk)})
S G SG SG函数描述的是博弈中某个状态量化后的值。其有以下性质:

  • 终止状态 S G SG SG值为 0 0 0,因为它没有出边(即可到达的状态):即所有 T e r m i n a l − p o s i t i o n Terminal-position Terminalposition所对应的顶点,也就是没有出边的顶点,其后续集合为空集,所以 S G SG SG值为 0 0 0
  • S G SG SG值为 0 0 0的状态为必败态,它的后继状态 S G SG SG值都不为 0 0 0,满足必败态定义:对于一个 S G ( x ) = 0 SG(x)=0 SG(x)=0顶点 x x x,其所有的后续 y y y都满足 S G ( y ) ≠ 0 SG(y)\neq 0 SG(y)=0
  • S G SG SG值不为 0 0 0的状态为必胜态,它的后继状态 S G SG SG值有为 0 0 0的状态,满足必胜态定义:对于一个 S G ( x ) ≠ 0 SG(x)\neq 0 SG(x)=0顶点 x x x,必定存在一个后续 y y y满足 S G ( y ) = 0 SG(y)=0 SG(y)=0

不妨再考虑以下顶点的 S G SG SG值的意义:

S G ( x ) = k SG(x)=k SG(x)=k时,表明对于任意一个 0 ⩽ i < k 0\leqslant i<k 0i<k,都存在 x x x的一个后继 y y y满足 S G ( y ) = i SG(y)=i SG(y)=i,也就是说当某个棋子的 S G SG SG值是 k k k时,我们可以转换成任意的 i i i,但绝不能保持 k k k不变。同时,在 N i m Nim Nim游戏中,每次都必须选择拿走石子,而不能保持 k k k不变。这表明,如果将 n n n枚棋子所在的顶点的 S G SG SG值看作 n n n堆相应数量的石子,那么这个 N i m Nim Nim游戏的每个必胜策略都对应于原来的这 n n n枚棋子的必胜策略。

对于 n n n个棋子,设它们对应的顶点的 S G SG SG值为 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,再设局面 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an时的 N i m Nim Nim游戏的一种必胜策略是将 a i a_i ai变为 k k k,那么原游戏中的一种必胜策略就是把第 i i i枚棋子移动到一个 S G SG SG值为 k k k的顶点。

接下来证明这种多棋子的有向图游戏的局面是 P − p o s i t i o n P-position Pposition当且仅当所有棋子所在的位置的 S G SG SG函数的异或和( N u m Num Num和)为 0 0 0即可。该证明与 N i m Nim Nim游戏中证明必胜策略( B o u t o n ′ s T h e o r e m Bouton's Theorem BoutonsTheorem)差不多相同。

定义有向图游戏的和:设 G 1 , G 2 , . . . , G n G_1,G_2,...,G_n G1,G2,...,Gn n n n个有向图游戏,定义游戏 G G G G 1 , G 2 , . . . , G n G_1,G_2,...,G_n G1,G2,...,Gn的和,游戏 G G G的移动规则是:任选择一个子游戏 G i G_i Gi并移动上面的棋子。则 S G ( G ) = S G ( G 1 ) ⊕ S G ( G 2 ) ⊕ . . . ⊕ S G ( G n ) SG(G)=SG(G_1)\oplus SG(G_2)\oplus...\oplus SG(G_n) SG(G)=SG(G1)SG(G2)...SG(Gn),即游戏的和的 S G SG SG值为其所有子游戏的 S G SG SG函数值的异或和。

那么一个局面为先手必败当且仅当该局面的 S G SG SG值为 0 0 0。同样如果游戏的和为 0 0 0,那么先手必败。

证明(SG):

之前有提到,任何一个 I C G ICG ICG都可以抽象成一个有向图游戏。所以 S G SG SG函数和游戏的和的概念就可以用于不止有向图游戏中了。我们给每个 I C G ICG ICG的每个局面定义 S G SG SG值,以及 n n n I C G ICG ICG的和。当面对由 n n n个游戏组成的一个游戏是,只需对于每个游戏找出求它的每个局面的 S G SG SG值的方法即可。

而如何求解 S G SG SG函数,根据定义可知:依次枚举每种状态并枚举每种状态能到达的状态,找到每个状态后继状态 S G SG SG值中没有出现的最小自然数就是它的 S G SG SG值。

综上:知道 S G SG SG函数和游戏的和后,我们就可以把复杂的游戏分解成若干个子游戏,对于每个子游戏找出它的 S G SG SG函数,然后全部异或和得到原游戏的 S G SG SG函数​,​就可以​优雅​地​解决​原游戏了😎。​


回到游戏规则改变后的 N i m Nim Nim问题:

题解:

  • 我们可以将其看作三个子游戏

  • 第一个游戏:只有一个堆,每次取出 1 , 2 1,2 1,2 3 3 3个石子:

    • 状态 0 的 S G 值为 0 ( 无石子 ) 状态 1 的 S G 值为 1 ( 取 1 石子 ) 状态 2 的 S G 值为 2 ( 取 2 石子 ) 状态 3 的 S G 值为 3 ( 取 3 石子 ) 状态 4 的 S G 值回到 0 ,因为取 1 , 2 , 3 可以回到之前已有状态 状态0的SG值为0(无石子)\\ 状态1的SG值为1(取1石子)\\ 状态2的SG值为2(取2石子)\\ 状态3的SG值为3(取3石子)\\ 状态4的SG值回到0,因为取1,2,3可以回到之前已有状态 状态0SG值为0(无石子)状态1SG值为1(1石子)状态2SG值为2(2石子)状态3SG值为3(3石子)状态4SG值回到0,因为取1,2,3可以回到之前已有状态

    • 易得: x x x个石子的 S G SG SG值为 x   m o d   4 x\bmod 4 xmod4

  • 第二个游戏:只有一个堆,每次取出奇数个石子:

    • 状态 0 的 S G 值为 0 状态 1 的 S G 值为 1 ( 取 1 个石子 ) 状态 2 的 S G 值为 0 ( 只能取奇数个,剩 1 后变成已知 S G 值的状态 ) 状态 3 的 S G 值为 1 ( 取 1 个石子到状态 2 ,或取 3 个到状态 0 ) 状态 0 的 SG 值为 0 \\ 状态 1 的 SG 值为 1(取 1 个石子)\\ 状态 2 的 SG 值为 0(只能取奇数个,剩 1 后变成已知 SG 值的状态)\\ 状态 3 的 SG 值为 1(取 1 个石子到状态 2,或取 3 个到状态 0) 状态0SG值为0状态1SG值为1(1个石子)状态2SG值为0(只能取奇数个,剩1后变成已知SG值的状态)状态3SG值为1(1个石子到状态2,或取3个到状态0)

    • x x x个石子的 S G SG SG值为 x   m o d   2 x\bmod 2 xmod2

  • 第三个游戏即为普通 N i m Nim Nim游戏了

根据 S G SG SG定理可得:
S G t o t a l = S G ( G 1 ) ⊕ S G ( G 2 ) ⊕ [ S G ( G 3 ) ⊕ . . . ⊕ S G ( G n ) ] SG_{total}=SG(G_1)\oplus SG(G_2)\oplus\left[SG(G_3)\oplus...\oplus SG(G_n)\right] SGtotal=SG(G1)SG(G2)[SG(G3)...SG(Gn)]
S G t o t a l = 0 SG_{total}=0 SGtotal=0,当前局面为必败局,反之为必胜局。


a n t i − S G anti-SG antiSG S J SJ SJ定理

相较于普遍 N i m Nim Nim游戏来说,变为了无法操作的人获胜,而进行最后一个操作的人失败。

经过分析,我们发现先手必胜有两种情况:

  • 每堆石子数异或和不为 0 0 0且至少有一堆石子数大于 1 1 1

  • 每堆石子数异或和为 0 0 0且每堆石子只有 1 1 1

证明:

  • 当所有堆的石子数的异或和为 0 0 0时,通常表示当前局面为一个“必败态”,即如果对方不犯错误,当前玩家将无法获胜。

    然而,在这种特殊情况下,当每堆只有 1 1 1 个石子,且异或和为 0 0 0,意味着所有的石子数都为 1 1 1 且总数为偶数(异或和为 0 0 0 表示奇偶平衡)。这种局面下,先手可以通过取走一颗石子,将剩下的石子数变为奇数个。此后,先手每次可以模仿后手的操作,以确保自己总是留下偶数个石子给对方,直到最终后手被迫拿走最后一颗石子而输掉比赛。

    因此,当异或和为 0 0 0 且每堆石子数为 1 1 1 时,先手可以通过这样策略性的操作确保后手取走最后一颗石子,从而获胜。

  • 综上:这两种情况之所以是先手必胜的关键原因在于:

    1. 异或和不为 0 且至少一堆石子数大于 1:先手可以通过操作将局面转变为对后手不利的状态,避免自己成为最后一个取石子的人。
    2. 异或和为 0 且每堆石子数为 1:先手可以控制局面,通过对称策略迫使后手成为最后一个取石子的人。
  • 类似于 S G SG SG定理,同样也有 S J SJ SJ定理:

    证明和 a n t i − N i m anti−Nim antiNim的证明相同,只不过对于第二种情况一个 S G SG SG函数为 1 1 1的局势可以变成 S G SG SG函数大于 1 1 1的局势,只要将这种情况转为第一种情况就好了。


    multi-SG

    不妨计算过一下前几个状态的 S G SG SG值:
    s g ( 1 ) = m e x { s g ( 0 ) } s g ( 2 ) = m e x { s g ( 0 ) , s g ( 1 ) } = 2 s g ( 3 ) = m e x { s g ( 0 ) , s g ( 1 ) , s g ( 2 ) , s g ( 1 , 2 ) } = 4 s g ( 4 ) = m e x { s g ( 0 ) , s g ( 1 ) , s g ( 2 ) , s g ( 1 , 3 ) , s g ( 2 , 2 ) , s g ( 3 ) } = 3 sg(1) = mex\{sg(0)\}\\ sg(2) = mex\{sg(0), sg(1)\} = 2\\ sg(3) = mex\{sg(0), sg(1), sg(2), sg(1,2)\} = 4\\ sg(4) = mex\{sg(0),sg(1), sg(2), sg(1,3), sg(2,2), sg(3)\} = 3 \\ sg(1)=mex{sg(0)}sg(2)=mex{sg(0),sg(1)}=2sg(3)=mex{sg(0),sg(1),sg(2),sg(1,2)}=4sg(4)=mex{sg(0),sg(1),sg(2),sg(1,3),sg(2,2),sg(3)}=3
    综上可得:
    S G ( x ) = { x − 1 ( x m o d    4 = 0 ) x ( x m o d    4 = 1 ∨ 2 ) x + 1 ( x m o d    4 = 3 ) SG(x) = \begin{cases} x - 1 & (x \mod 4 = 0) \\ x & (x \mod 4 = 1 \vee 2) \\ x + 1 & (x \mod 4 = 3) \end{cases} SG(x)= x1xx+1(xmod4=0)(xmod4=12)(xmod4=3)


    every-SG

    e v e r y − S G every−SG everySG游戏规定对于任意未结束的单一游戏,游戏者必须对它进行决策,游戏结束时间决定于最后一个结束的单一游戏,胜负则取决于最后一个结束的单一游戏的输赢。

    对于每一步决策,因为一直 S G SG SG值为 0 0 0时必败, S G SG SG值不为 0 0 0时必胜,所以我们一定希望 S G SG SG 0 0 0的局势尽快结束, S G SG SG不为 0 0 0的局势尽量慢点结束。

    所以需要知道 S G SG SG值为 0 0 0的局势最少多少步结束, S G SG SG值不为 0 0 0的局势最多多少步结束。

    我们用 s t e p step step函数来记录这个值:
    s t e p ( u ) = { 0 , u为终止状态 max ⁡ { s t e p ( v ) } , s g ( u ) ≠ 0 ∧ v 为 u 的后继 ∧ s g ( v ) = 0 min ⁡ { s t e p ( v ) } , s g ( u ) = 0 ∧ v 为 u 的后继 step(u) = \begin{cases} 0, & \text{u为终止状态} \\ \max\{step(v)\}, & sg(u) \neq 0 \land v为u的后继 \land sg(v) = 0 \\ \min\{step(v)\}, & sg(u) = 0 \land v为u的后继 \end{cases} step(u)= 0,max{step(v)},min{step(v)},u为终止状态sg(u)=0vu的后继sg(v)=0sg(u)=0vu的后继
    对于every-SG游戏先手必胜当且仅当单一游戏step最大的一个为奇数。

    举报

    相关推荐

    0 条评论