zoukankan      html  css  js  c++  java
  • 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
    ");
            else printf("No
    ");
        }
        return 0;
    }
  • 相关阅读:
    WPF程序国际化
    MVVM框架搭建
    最全前端开发面试问题及答案整理
    最小化运行批处理
    C#中App.config文件配置获取
    VS2017 打包成exe
    Inno Setup生成桌面快捷方式
    C#文件读写(txt 简单方式)
    Flume 学习笔记之 Flume NG概述及单节点安装
    快学Scala 第二十课 (trait的构造顺序)
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10820403.html
Copyright © 2011-2022 走看看