题目传送门

一、Nim游戏介绍

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

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

据说,它源自中国,经由被贩卖到美洲的奴工们外传。辛苦的工人们,在工作闲暇之余,用石头玩游戏以排遣寂寞。后来流传到高级人士,则用便士(Pennies),在酒吧柜台上玩。

最有名的玩法,是把十二枚便士放成\(3、4、5\)三列,拿光铜板的人赢。

后来,大家发现,先取的人只要在\(3\)那列里取走\(2\)枚,变成了\(1、4、5\),就能稳操胜券了,游戏也就变得无趣了。

于是大家就增加列数,增加铜板的数量,这样就让人们有了毫无规律的感觉,不易于把握。

二、举个栗子

  • \(2\)\(3\)两堆石子,甲先来,从\(3\)那堆中取\(1\)个,留下两堆,分别是\(2\)\(2\)

  • 以后不管乙怎么拿,甲都和他在相反的一堆中做同样的操作,这样,肯定是乙最先面对\(0\)\(0\)状态。

三、结论

在两名选手都足够聪明,每一步都是最优解的情况下:
\(a_1\) ^ \(a_2\) ^ ... ^\(a_n=0\) 先手必败
\(a_1\) ^ \(a_2\) ^ ... ^\(a_n\neq0\) 先手必胜
就是把所有堆的石子个数异或起来,结果是零,先手必败,不是零,先手必胜。

四、扩展问题

如果\(Nim\)游戏中的规则稍微变动一下,每次最多只能取\(K\)个,怎么处理?

方法是将每堆石子数\(mod\ (k+1)\).

五、C++ 代码

#include <bits/stdc++.h>

using namespace std;
const int N = 100010;

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    int n;
    cin >> n;

    int res = 0;    //起始值是0,因为任何数与0进行异或都它本身
    while (n--) {
        int x;
        cin >> x;
        res ^= x;
    }
    if (res) puts("Yes"); //异或值是非零,则可以先手通过某种办法将异或值变为零
    else puts("No");      //异或值是零,必败
    return 0;
}

相关文章: