HDU 3537 Daizhenyang's Coin 翻硬币博弈
题意:
给你n个硬币,你可以从中拿出来1、2、3个硬币,它们不一定要连续,你只需要保证拿出来的硬币中那个下标最大的硬币一定要是正面朝上,最后谁不能操作,谁就输了
题解:
结论:
局面的SG 值为局面中每个正面朝上的棋子单一存在时的SG 值的异或和。即一个有k个硬币朝上,朝上硬币位置分布在的翻硬币游戏中,SG值是等于k
个独立的开始时只有一个硬币朝上的翻硬币游戏的SG值异或和。比如THHTTH这个游戏中,2号、3号、6号位是朝上的,
它等价于TH、TTH、TTTTTH三个游戏和,即sg[THHTTH]=sg[TH]^sg[TTH]^sg[TTTTTH].我们的重点就可以放在单个硬币朝上时的SG值的求法。
(本篇文章T代表反面朝上,H相反)
根据上面的结论我们可以对题目上输入的THTTHT(假设的一个),SG(THTTHT)=SG(TH)^SG(TTTTH),因此我们只需要求出来H、TH、TTH、TTTH......所有的SG值就可以解决这一道题
初始化SG(T)=0
SG(H),H可以一次操作变成T,所以SG(H)=mex{SG(T)}=mex{0}=1 //mex就是求最小的不在这里面的值,不是负值
SG(TH),可以一次操作变成TT,HT,所以SG(TH)=mex{SG(TT),SG(HT)}=mex{SG(T),SG(H)}=mex{0,1}=2 //根据题意取出来硬币下标最大的应该是H,所以靠右边的T都没用
SG(TTH),可以一次操作变成HHT,TTT,THT,HTT,SG(TTH)=mex{SG(HH),SG(T),SG(TH),SG(H)}=mex{SG(H)^SG(TH),SG(T),SG(TH),SG(H)}
mex{1^2,0,2,1}=mex{3,0,2,1}=4;
通过求sg发现规律
sg 1 2 4 7 8 11 13 14
x 0 1 2 3 4 5 6 7
找到规律,sg[x],如果x的二进制1的个数为奇数,sg[x]=2*x ,否则 sg[x]=2*x+1;
然后把各个Sg的值异或最终就是答案
找规律代码:
1 #include <bits/stdc++.h> 2 3 4 5 using namespace std; 6 7 8 9 const int MAXN = 10005; 10 11 12 13 int SG[MAXN]; 14 15 bool SGhash[MAXN]; 16 17 18 19 inline int getSG(int x){ 20 21 memset(SGhash,false,sizeof SGhash); 22 23 SGhash[0] = true;//翻一个硬币 24 25 for(int i=0 ; i<x ; ++i)SGhash[SG[i]] = true;//翻两个硬币 26 27 for(int i=0 ; i<x ; ++i) 28 29 for(int j=0 ; j<i ; ++j)SGhash[SG[i]^SG[j]] = true;//翻三个硬币 30 31 for(int i=0 ; ; ++i)if(!SGhash[i])return i; 32 33 } 34 35 36 37 inline void BuildSG(int x){ 38 39 memset(SG,0,sizeof SG); 40 41 SG[0] = 1;//当head-up硬币位置在0时先手必胜 42 43 for(int i=1 ; i<=x ; ++i){ 44 45 SG[i] = getSG(i); 46 47 } 48 49 } 50 51 52 53 int main(){ 54 55 56 57 BuildSG(100); 58 59 for(int i=0 ; i<11 ; ++i)printf("%d:%d ",i,SG[i]); 60 61 printf("\n"); 62 63 for(int i=11 ; i<20 ; ++i)printf("%d:%d ",i,SG[i]); 64 65 66 67 return 0; 68 69 }
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <set> 4 using namespace std; 5 6 int sg(int x){ 7 int ans=0,tmp=x; 8 while( x>0 ){ 9 if( (x&1) ) ans++; 10 x/=2; 11 } 12 if( (ans&1) ) return 2*tmp; 13 else return 2*tmp+1; 14 } 15 16 int main(){ 17 int n,x; 18 while(scanf("%d",&n)!=EOF){ 19 int ans=0,x; 20 set <int> mys; 21 for(int i=0;i<n;i++){ 22 scanf("%d",&x); 23 mys.insert(x); 24 } 25 for(set <int>::iterator it=mys.begin();it!=mys.end();it++){ 26 ans^=sg(*it); 27 } 28 if(ans==0) printf("Yes\n"); 29 else printf("No\n"); 30 } 31 return 0; 32 }