zoukankan      html  css  js  c++  java
  • P2197 nim游戏

    传送门

    经典的博弈

    如果只有两堆

    那么结束状态就是(0,0)

    考虑先手

    怎样保持后手面对(0,0)

    如果两堆大小不一样

    那么先手只要保持两堆一样大就行了

    先手先取大的一堆

    使两堆一样大

    后手无论取多少

    先手只要在另一堆取一样多

    最后就一定是后手面对(0,0)

    但是如果两堆一样多...

    那先手取完

    就变成两堆不一样多,后手先取了

    那么先手必输

    那么对于很多堆的情况

    先手也要尽量使后手面对结束状态

    有一个结论:

    如果每堆的数量的异或和(xor,C语言中是" ^ ")不为 0

    那么先手必胜,反之先手必输

    证明:


    1. 对于总异或和 k ,如果不为 0

    对于 k 在2进制下的最高位 i 的 1

    肯定至少有一个原数 N 在2进制下的第 i 位为 1 (不然就不可能使 k 在第 i 位为1,原因请见 异或的定义

    所以如果我们要让 k 异或一个数 x ,使 k 变为 0,那么显然当x = k时是可以的

    所以如果我们把 N 成 N^k, 那么总异或和就变成 0 了(显然总异或和会变成 k^k = 0)

    显然N^k 一定是小于 N 的

    因为 N 和 k 的最高位 i 都为 1

    异或一下第 i 位就变成 0 了

    相当于变小了

    所以 N^k 一定小于 N

    所以只要把 N 减某个数就一定能变成 N^k;

    2. 如果 总异或和为 k 为 0

    那么如果你把任何一个堆的数减小任何一个数都会导致 k 变成不为 0 的数

    证明也不难

    把一个数减小,那么它至少其中一位会改变

    如果有一位改变了,那么异或和肯定也改变了(原因请见 异或的定义,稍微想一想就懂了)

    那异或和就不是 0 了;

    好了有了以上两点就容易了

    对于先手,如果异或和 k 不为 0

    那就把 k 变成 0

    那么后手就只能把 k 变成一个不为 0 的数

    那先手就再把 k 变为 0

    一直减下去

    当所有数都减完了

    k=0,肯定是后手面对这种状态

    所以先手必胜

    反之

    如果一开始异或和 k 为 0...

    那么先手只能让 k 变成不为 0 的数

    然后后手笑了...

    先手就必输了


    证明完毕

    有了结论就很简单了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int t,n,a,k;
    int main()
    {
        cin>>t;
        while(t--)
        {
            k=0;
            cin>>n;
            for(int i=1;i<=n;i++)
            {
                cin>>a;
                k^=a;
            }
            if(k) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
  • 相关阅读:
    Linux下Java安装与配置
    HYSPLIT模式简介及在线平台使用
    HYSPLIT模式简介及单机版使用图文教程
    有关气象类资料数据下载网址
    Excel图表编程应用大全(2):图表编程基础
    当装系统时遇到“选中的磁盘采用GPT分区形式”
    SQL函数
    Centos6.7 Redis3.2.8的主从搭建
    Centos 6.7 中 Redis-3.2.8的安装
    Mysql innodb_buffer_pool_size的研究
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9560201.html
Copyright © 2011-2022 走看看