Nim游戏

Nim游戏

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

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

输入格式

第一行包含整数 $n$。

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

输出格式

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

否则,输出 No 。

数据范围

$1 \leq n \leq {10}^5$,
$1 \leq \text{每堆石子数} \leq {10}^9$

输入样例:

2
2 3

输出样例:

Yes

 

解题思路

  最基础的博弈论,主要补充证明。

  先介绍两种状态,必胜态必败态,这两种状态都是对于先手而言的

  1. 必胜态,即当前状态为先手必胜的状态。在Nim游戏中指的是先手拿完石子后的所有状态中,存在一个状态是必败态。此时由于下一个状态是对手先手必败态,所以当前的状态就是先手必胜态。
  2. 必败态,即当前状态为先手必败的状态。在Nim游戏中指的是先手拿完石子后的所有状态中,不存在一个状态是必败态(即所有的状态都是对手先手必胜态)。此时由于下一个状态均是对手先手必胜态,所以当前的状态就是先手必败态。

  先手必胜状态:可以走到某一个必败状态。先手必败状态:走不到任何一个必败状态。

  对于Nim游戏,先给出结论:有$n$堆石子分别为$a_1 \sim a_n$,如果有$a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$那么先手必败,否则$a_1 \oplus a_2 \oplus \cdots \oplus a_n \ne 0$那么先手必胜。

  当每一堆石子均为$0$,即$a_1 = a_2 = \cdots = a_n = 0$,那么很明显先手必败,此时有$a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$。

  当$a_1 \oplus a_2 \oplus \cdots \oplus a_n = x \ne 0$,证明一定可以从某一堆中拿走若干个石子使得异或值为$0$。假设$x$的二进制最高位的$1$是第$k$位,因此$a_1 \sim a_n$中必然至少存在一个数$a_i$,$a_i$的二进制第$k$位为$1$。因此有$a_i \oplus x < a_i$,这是因为$a_i \oplus x$的结果的第$k$位为$0$,而更高位的数与$a_i$相同(更高位即大于$k$的高位,$x$的更高位均为$0$,因此异或后的结果与$a_i$相同),故$a_i \oplus x < a_i$。然后我们从$a_i$这堆中拿走$a_i - (a_i \oplus x)$个石子,就变成了$a_i' = a_i - \left( {a_i - (a_i \oplus x)} \right) = a_i \oplus x$。剩下的数的异或结果就是

\begin{align*}
& \ \ \ \ \ a_1 \oplus a_2 \oplus \cdots \oplus a_i' \oplus \cdots \oplus a_n \\
&= a_1 \oplus a_2 \oplus \cdots \oplus (a_i \oplus x) \oplus \cdots \oplus a_n \\
&= a_1 \oplus a_2 \oplus \cdots \oplus a_n \oplus x \\
&= x \oplus x \\
&= 0
\end{align*}

  故如果异或值不为$0$那么一定存在一种操作方式使得异或值变成$0$。意味着如果先手遇到异或值不为$0$的状态,那么一定可以转换为让对手先手必败的状态。

  如果$a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$(其中$a_1 \sim a_n$不全为$0$),那么不管从哪堆石子中拿多少石子,剩下的数的异或值必然不为$0$。反证法,假设从第$i$堆中石子拿完石子后变成了$a_i' \ne a_i$,并且$a_1 \oplus a_2 \oplus \cdots \oplus a_i' \oplus \cdots \oplus a_n \ne 0$,将该式子与$a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$等号两边进行异或,就会得到$a_i \oplus a_i' = 0$,即$a_i = a_i'$(等号两边同时异或$a_i'$),就矛盾了,因为我们至少要拿一个石子。

  因此现在我们得到$3$个结论:

  1. 没有后继状态的状态是必败状态。
  2. 对于$a_1 \oplus a_2 \oplus \cdots \oplus a_n \ne 0$的状态,一定存在某种移动使得 $a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$。
  3. 对于$a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$的状态,一定不存在某种移动使得$a_1 \oplus a_2 \oplus \cdots \oplus a_n \ne 0$。

  这就说明如果先手时异或值不为$0$,那么一定可以变成异或值为$0$的状态给后手,而后手不管怎么拿都一定会变成异或值不为$0$的状态给先手,重复该过程,可以发现先手总是处于不为$0$的状态而后手总是处于为$0$的状态,由于最终全部石子都会被拿完,且后手的异或值总是为$0$,因此先手必胜而后手必败。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int main() {
 5     int n;
 6     scanf("%d", &n);
 7     int ret = 0;
 8     while (n--) {
 9         int x;
10         scanf("%d", &x);
11         ret ^= x;
12     }
13     printf("%s", ret ? "Yes" : "No");
14     
15     return 0;
16 }

 

参考资料

  AcWing 891. Nim游戏:https://www.acwing.com/video/312/

  博弈论 - OI Wiki:https://deploy-preview-980--oi-wiki.netlify.app/math/game-theory/

posted @ 2023-02-15 23:35  onlyblues  阅读(31)  评论(0编辑  收藏  举报
Web Analytics