bzoj2437 [Noi2011]兔兔与蛋蛋

来源:互联网 发布:服务端数据为空淘宝 编辑:程序博客网 时间:2024/04/28 21:18

题解:我们将棋盘分成黑白格子(相邻格子颜色不同),将空格染成黑色,那么我们可以知道X为合法棋子当且仅当X在黑色格子,而O为合法棋子当且仅当O在白色格子。

相邻合法棋子(空格也为合法)连边,则我们得到无向图,而它也是二部图,当空格一定是在最大匹配中时,则先手必胜;否则先手必败。若不一定在,则空格可能在偶数条交错路,或者有点可以代替空格(此时空格可不在最大匹配中),那么此时后手总有办法走到先手所在的部,即先手无法移动。

判断空格是否在最大匹配中,若空格没有对应的匹配点,那么空格肯定不在最大匹配中;若有匹配点,则把空格标记掉,同时将空格和对应匹配点的信息标为空,搜索匹配点看能否增大匹配数,若能则不一定在最大匹配中。

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <vector> using namespace std; const int N=45;char str[N][N];int num[N][N],cnt,tot;int del[N*N],vis[N*N],match[N*N],res[N*N];int Ans[2000];int xx,yy;int n,m;vector<int>V[N*N];bool dfs(int u){    for (int i=0;i<int(V[u].size());i++){        int v=V[u][i];        if (del[v] || vis[v]==tot)continue;        vis[v]=tot;        if (match[v]==0 || dfs(match[v])){            match[v]=u;match[u]=v;            return true;        }    }    return false;}void Add(int u,int v){    V[u].push_back(v);V[v].push_back(u);}int main(){    //freopen("1.txt","r",stdin);    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++){        scanf("%s",str[i]+1);        for (int j=1;j<=m;j++)if (str[i][j]=='.')            {xx=i;yy=j;}    }    str[xx][yy]='X';    memset(num,0,sizeof num);    cnt=0;    for (int i=1;i<=n;i++)for (int j=1;j<=m;j++){            if (str[i][j]=='X' && (abs(xx-i)+abs(yy-j))%2==0)                cnt++,num[i][j]=cnt;            if (str[i][j]=='O' && (abs(xx-i)+abs(yy-j))%2==1)                cnt++,num[i][j]=cnt;    }    for (int i=1;i<=cnt;i++)V[i].clear();    for (int i=1;i<=n;i++)for (int j=(i%2==0)?2:1;j<=m;j+=2){        if (j>1 && num[i][j-1])Add(num[i][j],num[i][j-1]);        if (j<m && num[i][j+1])Add(num[i][j],num[i][j+1]);        if (i>1 && num[i-1][j])Add(num[i][j],num[i-1][j]);        if (i<n && num[i+1][j])Add(num[i][j],num[i+1][j]);    }    memset(vis,0,sizeof vis);    memset(del,0,sizeof del);    memset(match,0,sizeof match);    tot=0;    for (int i=1;i<=n;i++)for (int j=(i%2==0)?2:1;j<=m;j+=2)/*if (num[i][j] && !match[num[i][j]])*/{//只能从二分图的一边搜,为什么不能加/**/里面的部分        tot++;dfs(num[i][j]);    }    int Q;scanf("%d",&Q);Q<<=1;    for (int i=1;i<=Q;i++){        if (match[num[xx][yy]]==0){            res[i]=0;//i走之前预判必败还是必胜,0表示必败            del[num[xx][yy]]=1;        }else{            int tmp;            match[tmp=match[num[xx][yy]]]=0;match[num[xx][yy]]=0;            del[num[xx][yy]]=1;            tot++;res[i]=!dfs(tmp);        }        scanf("%d%d",&xx,&yy);    }    int gs=0;    for (int i=1;i<=Q;i+=2)if (res[i] && res[i+1])gs++,Ans[gs]=(i+1)>>1;    printf("%d\n",gs);    for (int i=1;i<=gs;i++)printf("%d\n",Ans[i]);    return 0;}



0 0
原创粉丝点击