L - River Game(博弈 SG函数)

有些话、适合烂在心里 提交于 2020-03-01 19:07:30

https://vjudge.net/problem/Gym-102501L

题意:

L题是个游戏,NNN\cdot N网格中,有连通(上下左右)的星,每块连通的星会接触左边界和右边界,大小至多2N。两块连通的星的任意两个点之间距离至少为3。

现在轮流放置照相机,需要接触星,接触同一块连通的星的照相机不能接触,x不能放置,问先手赢还是后手赢。

解析:

距离为3说明两个大块之间的game独立。一个大块上放照相机不能互相接触,说明可以将可以放照相机的’.'块通过连通性再次分割。

最后对于一个连通的’.‘块,由于保证一个大块最多只有20个点,所以’.'块感觉也差不多20个。

所以可以用状压dp跑SG函数,代码中的deal2函数处理的是这个。二进制中,1代表可以放,0代表已经被放置或与放置的相邻。答案的SG值为所有SG值的异或。SG不为0时先手赢。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-03-01-14.47.40
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
#define debug(x) cerr<<#x<<" = "<<x<<'\n'
const LL mod=1e9+7;
const int maxn=1e7+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/
int n;
char x[13][13];
bool vis[13][13];

bool isw[13][13];

int di[4][2]={1,0,-1,0,0,1,0,-1};
void dfs(int a,int b){
    vis[a][b]=1;
    isw[a][b]=1;
    rep(i,0,3){
        int A=a+di[i][0],B=b+di[i][1];
        if(A<1||A>n||B<1||B>n||x[A][B]!='*'||vis[A][B])continue;
        dfs(A,B);
    }
}

vector<pill>V;
int fa[109];
int fin(int p){return fa[p]==p?p:fa[p]=fin(fa[p]); }
int un(int a,int b){
    int f1=fin(a),f2=fin(b);
    if(f1!=f2)
        fa[f1]=f2,fin(f1);
}
bool isNear(pill a,pill b){
    rep(i,0,3){
        pill tmp=a;
        tmp.fi+=di[i][0],tmp.se+=di[i][1];
        if(tmp.fi==b.fi&&tmp.se==b.se)return 1;
    }
    return 0;
}

vector<pill>VV;
int dp[maxn];
int deal2(){// 联通的'.'
    int sta[109];
    mmm(sta,0);
    int n=VV.size();
    rep(i,0,n-1){
        sta[i]|=1<<i;
        rep(j,0,n-1){
            if(isNear(VV[i],VV[j])){
                sta[i]|=1<<j;
            }
        }
    }
    dp[0]=0;
    int MS=(1<<n)-1;
    assert(MS+1<maxn);
    rep(i,1,MS){
        bool vis[30];
        mmm(vis,0);
        rep(j,0,n-1){
            if(i&(1<<j)){
                int To=i&(MS-sta[j]);
                assert(To<i);
                vis[dp[To]]=1;
                assert(dp[To]+1<30);
            }
        }
        for(int now=0;;now++){
            if(!vis[now]){
                dp[i]=now;break;
            }
        }
    }
    return dp[MS];
}

int deal(){// 与一块*联通的点
    int n=V.size();
    rep(i,0,n-1){
        fa[i]=i;
    }
    rep(i,0,n-1){
        rep(j,i+1,n-1){
            if(isNear(V[i],V[j]))
                un(i,j);
        }
    }
    int SG=0;
    rep(i,0,n-1){
        VV.clear();
        rep(j,0,n-1){
            if(fin(j)==i)VV.pb(V[j]);
        }
        if(VV.size())
            SG^=deal2();
    }
    return SG;
}

int main(){
    n=rd;
    rep(i,1,n){
        scanf("%s",x[i]+1);
    }
    int SG=0;
    rep(i,1,n)rep(j,1,n){
        mmm(isw,0);
        if(vis[i][j]||x[i][j]!='*')continue;
        dfs(i,j);
        V.clear();
        rep(I,1,n)rep(J,1,n){// 空地
            if(x[I][J]!='.')continue;
            int is=0;
            rep(_,0,3){
                int II=I+di[_][0];
                int JJ=J+di[_][1];
                if(isw[II][JJ]){is=1;break;}
            }
            if(is)
                V.pb({I,J});
        }
        if(V.size()){
            SG^=deal();
        }
    }
    if(SG!=0){
        printf("First player will win\n");
    }
    else{
        printf("Second player will win\n");
    }

    return 0;
}

/*_________________________________________________________end*/
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!