bzoj1188 [HNOI2007]分裂游戏

做了两道取石子就以为自己会了博弈论,我真是个SB

我理解的$SG$定理:

每个局面分成若干个子游戏,一个大局面的$SG$函数是每个小游戏$SG$函数的异或值

同时每个小游戏又有对应的的后继局面,那么这个游戏的$SG$函数就是所有后继局面$SG$函数的$mex$

当$SG$函数为0时,该游戏(局面)必败,否则必胜

那么这道题我们把每个石子当作一个小游戏,把一堆石子当作一个局面,每个石子位置的SG函数就是他能到达的所有后继局面的$mex$,发现两个石子只有他们位置不同时,$SG$函数才有可能不同,所以位置就是关键字,每个石子又能转移到若干个两个石子的局面,那么他的$SG$值就可以算了,最后的结果只需要枚举首次移动就好了。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 bool bo[105];
 8 int sg[25],T,n,a[25];
 9 int getsg(int x){
10     memset(bo,0,sizeof bo);
11     for(int i=1;i<x;i++)
12         for(int j=1;j<=i;j++)
13             bo[sg[i]^sg[j]]=1;
14     for(int i=0;;i++)if(!bo[i])return i;
15 }
16 int main(){
17     for(int i=1;i<=22;i++)sg[i]=getsg(i);
18     scanf("%d",&T);
19     while(T--){
20         scanf("%d",&n);
21         int now=0,tot=0;
22         for(int i=1;i<=n;i++){
23             scanf("%d",&a[i]);
24             if(a[i]&1)now^=sg[n-i+1];
25         }
26         for(int i=1;i<=n;i++)if(a[i])
27             for(int j=i+1;j<=n;j++)
28                 for(int k=j;k<=n;k++)
29                     if(!(now^sg[n-i+1]^sg[n-j+1]^sg[n-k+1])){
30                         if(!(tot++))printf("%d %d %d\n",i-1,j-1,k-1);
31                     }
32         if(!tot)puts("-1 -1 -1");
33         printf("%d\n",tot);
34     }
35     return 0;
36 }
View Code

 

posted @ 2018-01-22 19:56  Ren_Ivan  阅读(112)  评论(0编辑  收藏  举报