HDU 3537 Daizhenyang's Coin (Mock Turtles 游戏)

来源:互联网 发布:小型局域网网络搭建 编辑:程序博客网 时间:2024/05/19 23:53

题目大意:

就是现在有一些放成一排, 其中有一部分正面向上, 一部分反面向上, 每次可以选择1~3个硬币进行翻转, 选择翻转的硬币中最右边的那个必须是原本正面朝上的

谁不能翻转了就输了

问谁会获得胜利


大致思路:

其实这个问题是翻硬币的游戏当中经典的问题之一

首先需要知道这样一个事实:

如果现在又1~n个硬币有的正面朝上有的反面朝上, 每次只能翻转一个正面朝上的硬币然后可以选择这个硬币左边的一个硬币也一起翻转(也可以不选择一起翻转)

这个游戏的SG函数等同于将所有正面朝上的硬币用其编号数量的一堆牌代替之后的Nim游戏, (用游戏的和考虑操作的等价性)

理解上面那个问题之后, 有这样一个事实:

一般反应比游戏的一个结论:

一般规则

第一,游戏者根据某些约束翻硬币,但他所翻动的硬币中,最右边那个硬币的必须是从正面翻到反面。例如,只能翻3个硬币的情况,那么第三个硬币必须是从正面翻到反面。如果局面是正正反,那就不能翻硬币了,因为第三个是反的。


第二,谁不能翻谁输。


    有这样的结论:局面的SG 值为局面中每个正面朝上的棋子单一存在时的SG 值的异或和。即一个有k个硬币朝上,朝上硬币位置分布在的翻硬币游戏中,SG值是等于k个独立的开始时只有一个硬币朝上的翻硬币游戏的SG值异或和。比如THHTTH这个游戏中,2号、3号、6号位是朝上的,它等价于TH、TTH、TTTTTH三个游戏和,即sg[THHTTH]=sg[TH]^sg[TTH]^sg[TTTTTH].我们的重点就可以放在单个硬币朝上时的SG值的求法。


也可以用上面那个游戏的和一样的方式理解


然后就是Mock Turtles 游戏的模型:

每次可以翻动一个、二个或三个硬币。(Mock Turtles游戏)

初始编号从0开始。

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

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

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

位置x:0  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。所以1,2,4,7是odious因为它们的二进制形式是1,10,100,111.而0,3,5,6是evil,因为它们的二进制形式是0,11,101,110。而上面那个表中,貌似sg值都是odious数。所以当2x为odious时,sg值是2x,当2x是evil时,sg值是2x+1.

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

                                                      evil^evil=odious^odious=evil

                                                      evil^odious=odious^evil=odious

假设刚才的假说是成立的,我们想证明下一个sg值为下一个odious数。注意到我们总能够在第x位置翻转硬币到达sg为0的情况;通过翻转第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+1,sg[x]又是偶数个,那么x1^x2^…^xn=0。相反,如果x1^x2^…^xn=0且n是偶数,那么sg[x1]^…^sg[xn]=0。这个如果不太理解的话,我们可以先这么看下。2x在二进制当中相当于把x全部左移一位,然后补零,比如说2的二进制是10,那么4的二进制就是100。而2x+1在二进制当中相当于把x全部左移一位,然后补1,比如说2的二进制是10,5的二进制是101。现在看下sg[x1]^…^sg[xn]=0,因为sg[x]是2x或者2x+1,所以式子中的2x+1必须是偶数个(因为2x的最后一位都是0,2x+1的最后一位都是1,要最后异或为0,2x+1必须出现偶数次)


以上Mock Turtles游戏的分析参见于:常见翻硬币游戏总结


于是根据以上分析, 将输入的位置去重之后判断是否是偶数个位置以及异或和是否是0即可知道是否是P点了


代码如下:

Result  :  Accepted     Memory  :  1604 KB     Time  :  15 ms

/* * Author: Gatevin * Created Time:  2015/5/4 19:51:52 * File Name: Rin_Tohsaka.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endlint n, a[110];map<int, bool> M;int main(){    while(scanf("%d", &n) != EOF)    {        M.clear();        for(int i = 0; i < n; i++)            scanf("%d", a + i);        int cnt = 0;        int sg = 0;        for(int i = 0; i < n; i++)            if(!M[a[i]]) cnt++, M[a[i]] = 1, sg ^= a[i];        if(!(cnt & 1) && sg == 0)            puts("Yes");        else puts("No");    }    return 0;}


0 0
原创粉丝点击