Nim游戏
给定 $n$ 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 $n$。
第二行包含 $n$ 个数字,其中第 $i$ 个数字表示第 $i$ 堆石子的数量。
输出格式
如果先手方必胜,则输出 Yes 。
否则,输出 No 。
数据范围
$1 \leq n \leq {10}^5$,
$1 \leq \text{每堆石子数} \leq {10}^9$
输入样例:
2 2 3
输出样例:
Yes
解题思路
最基础的博弈论,主要补充证明。
先介绍两种状态,必胜态和必败态,这两种状态都是对于先手而言的。
- 必胜态,即当前状态为先手必胜的状态。在Nim游戏中指的是先手拿完石子后的所有状态中,存在一个状态是必败态。此时由于下一个状态是对手先手必败态,所以当前的状态就是先手必胜态。
- 必败态,即当前状态为先手必败的状态。在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 + 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$个结论:
- 没有后继状态的状态是必败状态。
- 对于$a_1 \oplus a_2 \oplus \cdots \oplus a_n \ne 0$的状态,一定存在某种移动使得 $a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0$。
- 对于$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/
标签:状态,游戏,Nim,必败,石子,异或,cdots,oplus From: https://www.cnblogs.com/onlyblues/p/17125162.html