AcWing 891. Nim游戏

题目传送门

一、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 eq0) 先手必胜
就是把所有堆的石子个数异或起来,结果是零,先手必败,不是零,先手必胜。

四、扩展问题

如果(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;
}
原文地址:https://www.cnblogs.com/littlehb/p/15391869.html