Nim游戏初探

规则介绍

  • Nim游戏是一种两个人玩的游戏,玩家双方面对一堆硬币(或者石头或者豆粒)。假设有 k ≥ 1 k\geq 1 k1堆硬币,每堆分别有 n 1 , n 2 , n 3 . . . n k n_1,n_2,n_3...n_k n1,n2,n3...nk枚硬币,游戏的目标是取得最后一枚硬币。游戏规则如下
    (1)玩家轮番出场第一个取子的玩家称为1号,第二个玩家称为二号
    (2)当轮到一个玩家取子时,他们都要从选择的硬币堆中至少取走一枚硬币,也就是说拿哪一堆都可以,可以把这一堆硬币都拿走
    当硬币都拿完的时候游戏结束,最后一个拿的玩家获胜
  • 现在给定硬币堆数 k k k和每堆硬币数量分别为 n 1 , n 2 . . . n k n_1,n_2...n_k n1,n2...nk,假设每步两人都选择最优方案,问1号赢还是2号赢

思路

我们可以考虑最简单的情况

  1. 只有一堆硬币,此时1号玩家只需要全部拿走即可,1号获胜
  2. 有两堆硬币,设数量分别为 n 1 n_1 n1 n 2 n_2 n2,如果 n 1 = n 2 n_1=n_2 n1=n2,假如说1号随便拿,如果我是2号,那么无论1号从哪一堆里面拿多少,我都从另外一堆里面拿走相同数量的硬币,这样我肯定是赢家;那如果 n 1 ≠ n 2 n_1\neq n_2 n1=n2呢?假如 n 1 > n 2 n_1\gt n2 n1>n2,如果我是一号,我要从第一堆里面拿走 n 1 − n 2 n_1-n_2 n1n2个,这样两堆又一样了,相当于刚才 n 1 = n 2 n_1=n_2 n1=n2的情况先手变后手,所以我一定获胜
  3. 经过前两个分析,能够感觉到如果堆数之间是平衡的那么1号必败,那么怎么能够得出一般性结论呢?

可以这样考虑

  • 将每堆硬币数量转换成一个二进制数,比如三堆硬币数量分别为3,5,8,那么转换为二进制数则分别为
    0011 0101 1000 0011\\0101\\1000 001101011000
  • 我们把每一位分开来看,某一位加一起如果是偶数那么就说这一位是平衡的,如果所有位都是平衡的那么就说这个游戏是平衡的,对于一个平衡的Nim游戏,1号玩家总是失败的,2号玩家总能胜利,因为1号玩家必定使得一个平衡游戏变得不平衡,2号玩家必定能使得1号玩家的不平衡游戏变得平衡
  • 用上面这个例子来说明,现在游戏显然是不平衡的,那么1号玩家可以对第三堆进行操作变成下面的情况
    0011 0101 0110 0011\\0101\\0110 001101010110
  • 现在就平衡了,那么2号玩家不管怎么拿,必然使得这个变得不平衡,这样继续拿下去,因为0是平衡的,所以最后拿的一定是1号玩家,也就是说1号玩家必然胜利,那么可以对所有堆的硬币数量进行异或操作,根据异或同出0,异出1的运算法则,如果游戏是平衡的,必然有异或最终结果为0,根据这个思路,可以解决这个问题,具体看下面的例题

例题

Nim模板题

  • 如果异或最终结果为0,那么1号失败,否则2号失败
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int main(){
    
    
    int n, t, x;
    cin >> t;
    while(t--){
    
    
        cin >> n;
        cin >> x;
        for(int i=1;i<n;i++){
    
    
            cin >> Data[i];
            x ^= Data[i];
        }
        if(x == 0){
    
    
            cout << "No" << "\n";
        }else cout << "Yes" << "\n";
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/115432801