UVa 1378 A Funny Stone Game [博弈论 SG函数]

A Funny Stone Game

题意:

$n \le 23$堆石子,每次选择$i < j \le k$,从$i$拿走1颗$j,k$各放入一颗,不能取就失败。求先手是否必胜以及第一次取的策略


 

一开始一直在想游戏怎么会结束...眼残没发现$i<j.....$

然后,解这类组合游戏问题重要的一步是发现独立的子游戏

本题中每个石子是互不影响的

$sg[i]$为一颗在$i$的石子的状态

$sg[i]=mex\{sg[j] \oplus sg[k]\}$

然后就$\oplus$起来就行了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=30;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}
int n,s[N];
int sg[N],use[N*N];
int main(){
    freopen("in","r",stdin);
    int cas=0;
    while( (n=read()) ){
        printf("Game %d: ",++cas);
        for(int i=1;i<=n;i++) s[i]=read();
        sg[n]=0;
        for(int i=n-1;i>=1;i--){
            memset(use,0,sizeof(use));
            for(int j=i+1;j<=n;j++)
                for(int k=j;k<=n;k++) use[ sg[j]^sg[k] ]=1;
            for(int j=0;;j++) if(!use[j]) {sg[i]=j;break;}
        }
        int ans=0;
        for(int i=1;i<=n;i++) if(s[i]&1) ans^=sg[i];
        if(ans!=0){
            int i,j,k;
            for(i=1;i<=n;i++) if(s[i]){
                int flag=0; ans^=sg[i];
                for(j=i+1;j<=n;j++){
                    if(ans==0) {k=j,flag=1;break;}
                    for(k=j+1;k<=n;k++) if( (ans^sg[j]^sg[k])==0 ) {flag=1;break;}
                    if(flag) break;
                }
                if(flag) break; ans^=sg[i];
            }
            printf("%d %d %d\n",i-1,j-1,k-1);
        }else puts("-1 -1 -1");
    }
}

 

posted @ 2017-03-13 16:23  Candy?  阅读(328)  评论(0编辑  收藏  举报