luogu 2197 Nim游戏

这是Nim游戏的一个入门认识:

Nim游戏是一个经典的博弈问题,在讨论这个问题之前,我们需要先知道一些博弈问题的基础知识,放在这里

接下来认为你已经知道了这些基础知识

首先介绍Nim游戏的含义:有n堆石子,每个人每次可以从一堆中取出一定数量的石子(可以全取走但不能不取),求是否存在先手必胜的策略,也就是初始局面是否是一个先手必胜局面

首先,Nim游戏一定能分出胜负,也就是说不会存在无法判断的情况(这一点很好理解:石子越取越少,所以任何一个状态总能结束)

而且,Nim游戏有一个特别好的结论:如果每一堆石子个数的异或和为0,那么这个局面就是先手必败的,否则就是先手必胜的!

证明:

我们把每一堆石子个数做二进制拆分,那么如果总异或和为0,那么可以说明每一位上1的总数是一个偶数

那么,我们假设先手取了$x$个石子,那么后手只需要对应地取一些石子,保证所有石子个数异或和仍为0即可

我们证明两点:

第一:为什么后手能取成这样的局面?

我们知道:原来的局势每一位上1的个数之和都是偶数,那么在某一堆里减去一部分之后,一定会有一些位置上1的个数和由偶数变成奇数(如果没有这样的位置,那么说明:原来为0的位置仍然为0(否则这一位上1的个数多了一个变成奇数),原来为1的位上仍然为1(否则这一位上1的个数少了一个变成奇数),那不就是没拿嘛!这是不合法的)

那么我们一定可以让另一堆石子也发生这样的变化

(这一点可以通过讨论得出:如果我们拿走了某一位上的1,那么我们一定也能找到一个值拿掉它对应位上的1,如果我们在某一位上多了一个1(这是减法退位导致的),那么我们要么可以在那个值这个对应位置上补一个1(因为同样去掉了最高位),要么可以把那一位上的1也拿掉,就能保证1的个数为偶数)

举个例子:比如我们把原来的$1011$,$110$,$101$,$1000$中的第一个变成了$100$,那么我们可以对应的把$1000$变成$100$,而如果原来的是$1011$,$110$,$1$,$1100$,那么我们可以$1100$变成$0$,都能满足条件

第二:为什么这样取是正确的?

因为先手取完之后,一定会导致全局异或和不为0,那么先手一定不能使石子个数变为0,而后手一直能保证全局异或和为0,由于游戏会结束,所以后手是必胜的

那么,如果起初全局异或和不为0,那么就是先手必胜了,因为先手一定可以设法使全局异或和为0,然后把后手变成先手,在全局异或和为零的情况下先手必败,于是此时先手就是必胜的

那么代码就呼之欲出了。

#include <cstdio>
int main()
{
    int T,n,a=0,x;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n),a=0;
        for(int i=1;i<=n;i++)scanf("%d",&x),a^=x;
        if(a)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

 

posted @ 2019-05-06 16:47  lleozhang  Views(150)  Comments(0Edit  收藏  举报
levels of contents