博弈论
BZOJ1188: [HNOI2007]分裂游戏
首先我们可以认为每一个石子都是一个独立的游戏(因为石子之间互不影响),那么我们用sg[i]表示处在i位置的一个石子的sg函数值,那么我们就可以枚举它的后继状态从而计算它的sg函数值。
最后累加ans即可。
1 int v[100],sg[100],a[100]; 2 int main() 3 { 4 freopen("input.txt","r",stdin); 5 freopen("output.txt","w",stdout); 6 int T=read(); 7 while(T--) 8 { 9 int n=read(); 10 sg[n-1]=0; 11 for3(i,n-2,0) 12 { 13 memset(v,0,sizeof(v)); 14 for2(j,i+1,n-1)for2(k,j,n-1)v[sg[j]^sg[k]]=1; 15 for(sg[i]=0;v[sg[i]];sg[i]++); 16 } 17 int ans=0; 18 for0(i,n-1) 19 { 20 a[i]=read(); 21 if(a[i]&1)ans^=sg[i]; 22 } 23 if(!ans)printf("%d %d %d\n%d\n",-1,-1,-1,0); 24 else 25 { 26 int tot=0; 27 for0(i,n-1) 28 for2(j,i+1,n-1) 29 for2(k,j,n-1) 30 if((ans^sg[i]^sg[j]^sg[k])==0) 31 { 32 if(!tot)printf("%d %d %d\n",i,j,k); 33 tot++; 34 } 35 printf("%d\n",tot); 36 } 37 } 38 return 0; 39 }
BZOJ1022: [SHOI2008]小约翰的游戏John
取完最后一个石子者败。先手必胜当且仅当:
1)存在某个a[i]>1,异或和!=0
2)不存在某个a[i]>1,异或和==0
脑补一下证明。不妨把状态补全
3)存在某个a[i]>1,异或和==0
4)不存在某个a[i]>1,异或和!=0
首先2)是必胜,4)是必败
考虑1)的状态,如果>1的a[i]只有一堆,我们就可把他变成4)让对手必败。
否则我们必定可以变成3)
而对于3)情况来说,一定存在至少两个a[i]>1,而每次只能对一堆石子进行操作,操作完了之后的状态一定是1)
我是在这里看的:链接君
(好像出现了循环论证?QAQ)
1 int main() 2 { 3 freopen("input.txt","r",stdin); 4 freopen("output.txt","w",stdout); 5 int T=read(); 6 while(T--) 7 { 8 int n=read(),ans=0,flag=0; 9 for1(i,n){int x=read();if(x>1)flag=1;ans^=x;} 10 if((ans&&flag)||(!ans&&!flag))printf("%s\n","John");else printf("%s\n","Brother"); 11 } 12 return 0; 13 }
POJ1704Georgia and Bob
我们发现每次给一个石子移动实际上是给后一个石子增加了移动的范围,也就是阶梯nim中的把一堆 石子中的某些放到前一堆里。于是阶梯nim就可以水过了!
1 int a[maxn],b[maxn]; 2 int main() 3 { 4 freopen("input.txt","r",stdin); 5 freopen("output.txt","w",stdout); 6 int T=read(); 7 while(T--) 8 { 9 int n=read(),ans=0; 10 for1(i,n)a[i]=read(); 11 sort(a+1,a+n+1); 12 for3(i,n-1,0)b[n-i]=a[i+1]-a[i]-1; 13 for1(i,n)if(i&1)ans^=b[i]; 14 printf("%s\n",!ans?"Bob will win":"Georgia will win"); 15 } 16 return 0; 17 }
POJ2960S-Nim
暴力枚举后继状态计算SG函数即可。
1 int a[maxn],v[maxn],sg[maxn]; 2 int main() 3 { 4 freopen("input.txt","r",stdin); 5 freopen("output.txt","w",stdout); 6 while(1) 7 { 8 int n=read();if(!n)break; 9 for1(i,n)a[i]=read(); 10 sg[0]=0; 11 for1(i,10000) 12 { 13 for1(j,n)if(i>=a[j])v[sg[i-a[j]]]=1; 14 for(sg[i]=0;v[sg[i]];sg[i]++); 15 for1(j,n)if(i>=a[j])v[sg[i-a[j]]]=0; 16 } 17 int T=read(); 18 while(T--) 19 { 20 int m=read(),ans=0; 21 for1(i,m)ans^=sg[read()]; 22 printf("%c",ans?'W':'L'); 23 } 24 printf("\n"); 25 } 26 return 0; 27 }
POJ3537Crosses and Crosses
我们发现如果取了中间i位置的石子,那么后手一定 i-2到i+2都不能取了,这样就分成了两个规模分别为i-3和n-i-2的NIM游戏。爆搜搞掉。
1 int sg[maxn],v[maxn]; 2 int main() 3 { 4 freopen("input.txt","r",stdin); 5 freopen("output.txt","w",stdout); 6 int n=read(); 7 sg[0]=0; 8 for1(i,n) 9 { 10 for1(j,i)v[(j-3>=0?sg[j-3]:0)^(i-j-2>=0?sg[i-j-2]:0)]=1; 11 for(sg[i]=0;v[sg[i]];sg[i]++); 12 for1(j,i)v[(j-3>=0?sg[j-3]:0)^(i-j-2>=0?sg[i-j-2]:0)]=0; 13 } 14 printf("%d\n",sg[n]?1:2); 15 return 0; 16 }
BZOJ2940: [Poi2000]条纹
sb题暴力sg。
1 int a[3],sg[maxn],v[maxn]; 2 int main() 3 { 4 freopen("input.txt","r",stdin); 5 freopen("output.txt","w",stdout); 6 for0(i,2)a[i]=read();sort(a,a+3); 7 for0(i,a[0]-1)sg[i]=0; 8 for2(i,a[0],1000) 9 { 10 for0(j,2)for1(k,i-a[j]+1)v[sg[k-1]^sg[i-k-a[j]+1]]=1; 11 for(sg[i]=0;v[sg[i]];sg[i]++); 12 for0(j,2)for1(k,i-a[j]+1)v[sg[k-1]^sg[i-k-a[j]+1]]=0; 13 } 14 int T=read(); 15 while(T--)printf("%d\n",sg[read()]?1:2); 16 return 0; 17 }