zoukankan      html  css  js  c++  java
  • hdu 3537(博弈,翻硬币)

    题意:给定了每个正面朝上的硬币的位置,然后每次可以翻1,2,3枚硬币,并且最右边的硬币开始必须是正面朝上的。

    分析:

    约束条件6:每次可以翻动一个、二个或三个硬币。(Mock Turtles游戏)

    初始编号从0开始。

    N==1时,硬币为:正,先手必胜,所以sg[0]=1.

    N==2时,硬币为:反正,先手必赢,先手操作后可能为:反反或正反,方案数为2,所以sg[1]=2

    N==3时,硬币为:反反正,先手必赢,先手操作后可能为:反反反、反正反、正反正、正正反,方案数为4,所以sg[2]=4

    位置x0  1  2  3  4   5    6   7    8     9  10  11  12  13  14...

    sg[x]  1  2  4  7  8  11 13 14  16  19  21  22  25  26  28…

    看上去sg值为2x或者2x+1。我们称一个非负整数为odious,当且仅当该数的二进制形式的1出现的次数是奇数,否则称作evil。所以1247odious因为它们的二进制形式是1,10,100,111.0,3,5,6evil,因为它们的二进制形式是0,11,101,110。而上面那个表中,貌似sg值都是odious数。所以当2xodious时,sg值是2x,当2xevil时,sg值是2x+1.

    这样怎么证明呢?我们会发现发现,

                                                          evil^evil=odious^odious=evil

                                                          evil^odious=odious^evil=odious

    假设刚才的假说是成立的,我们想证明下一个sg值为下一个odious数。注意到我们总能够在第x位置翻转硬币到达sg0的情况;通过翻转第x位置的硬币和两个其它硬币,我们可以移动到所有较小的evil数,因为每个非零的evil数都可以由两个odious数异或得到;但是我们不能移动到下一个odious数,因为任何两个odious数的异或都是evil数。

    假设在一个Mock Turtles游戏中的首正硬币位置x1,x2,…,xn是个P局面,即sg[x1]^^sg[xn]=0.那么无可置疑的是n必定是偶数,因为奇数个odious数的异或是odious数,不可能等于0。而由上面可知sg[x]2x或者2x+1sg[x]又是偶数个,那么x1^x2^^xn=0。相反,如果x1^x2^^xn=0n是偶数,那么sg[x1]^^sg[xn]=0。这个如果不太理解的话,我们可以先这么看下。2x在二进制当中相当于把x全部左移一位,然后补零,比如说2的二进制是10,那么4的二进制就是100。而2x+1在二进制当中相当于把x全部左移一位,然后补1,比如说2的二进制是105的二进制是101。现在看下sg[x1]^^sg[xn]=0,因为sg[x]2x或者2x+1,所以式子中的2x+1必须是偶数个(因为2x的最后一位都是0,2x+1的最后一位都是1,要最后异或为0,2x+1必须出现偶数次)。实际上的情况可能是这样的:

    MT游戏当中的P局面是拥有偶数堆石子的Nim游戏的P局面。

    这是翻硬币游戏里面种的一种,有了上面的理论之后这道题也就容易了!不过这道题要注意的地方就是去除重复位置!!

    代码实现:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int n, a[105];
    
    int main()
    {
        int flag, i, len, num, x;
        while(scanf("%d",&n)!=EOF)
        {
            flag=0;
            if(n==0)
            {
                printf("Yes
    ");
                continue;
            }
            for(i=0; i<n; i++)
                scanf("%d",&a[i]);
            sort(a,a+n);
            len=0;
            a[len++]=a[0];
            for(i=1;i<n;i++)
              if(a[i]!=a[len-1])
                  a[len++]=a[i];
    
            for(i=0; i<len; i++)
            {
                num=0;
                x=a[i]*2;
                while(a[i])
                {
                    if(a[i]&1)
                        num++;
                    a[i]=a[i]>>1;
                }
                if(num%2==0)
                    x++;
                flag=flag^x;
            }
    
            if(flag)
                printf("No
    ");
            else
                printf("Yes
    ");
        }
        return 0;
    }
  • 相关阅读:
    node入门(一)——安装
    移动web开发基础(二)——viewport
    移动web开发基础(一)——像素
    关于min-height:100%的解决办法
    用类与原型写一个组件(三)——学习笔记
    用类与原型写一个组件(二)——学习笔记
    用类与原型写一个组件(一)——学习笔记
    js类、原型——学习笔记
    Android 常用RGB值及名称
    AES加密示例
  • 原文地址:https://www.cnblogs.com/jiangjing/p/3764802.html
Copyright © 2011-2022 走看看