2019.01.06-dtoj-2437: [Noi2011]兔兔与蛋蛋

题目描述:


算法标签:二分图匹配

思路:

首先,看作是操作看成是空格移动,我叫空格的位置是起点。

如果对整个棋盘进行黑白染色,即所以与起点所在格子同色的黑色棋子和与起点所在格子不同色的白色格子才可以走到。于是对于棋盘中的有效点进行黑白匹配,每次移动会使走的那个点变成无效点。于是每次在二分图匹配中删去一个点再继续匹配。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=45,M=1605,dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
bool del[M],f[N][N],win[2005];char s[N][N];
int n,m,id[N][N],tot,si,sj,nt,d[M],mat[M],head[M],ne[M<<1],to[M<<1],cnt;
il int read(){int x;char ch;_(!);x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return x;}
il void insert(int x,int y){ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;}
il bool dfs(int x){
    d[x]=nt;
    for(int i=head[x];i;i=ne[i]){
        if(del[to[i]]||d[to[i]]==nt)continue;
        d[to[i]]=nt;
        if(!mat[to[i]]||dfs(mat[to[i]])){
            mat[to[i]]=x;mat[x]=to[i];
            return 1;
        }
    }
    return 0;
}
int main()
{
    n=read();m=read();for(int i=1;i<=n;i++)scanf(" %s",s[i]+1);
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
        if(s[i][j]=='.'){si=i;sj=j;s[i][j]='X';break;}
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
        if(s[i][j]=='O'&&((abs(si-i)+abs(sj-j))&1))id[i][j]=++tot;
        if(s[i][j]=='X'&&((abs(si-i)+abs(sj-j))&1)==0)id[i][j]=++tot;
    }
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
        if(id[i][j])for(int k=0;k<4;k++){
            int xx=i+dx[k],yy=j+dy[k];
            if(id[xx][yy])insert(id[i][j],id[xx][yy]);
        }
    for(int i=1;i<=tot;i++)if(!mat[i])++nt,dfs(i);
    int q=read();q<<=1;
    for(int i=1;i<=q;i++){
        int x=id[si][sj];
        if(mat[x]){int v=mat[x];mat[x]=mat[v]=0;del[x]=1;nt++;win[i]=!dfs(v);}
        else del[x]=1;
        si=read(),sj=read();
    }
    int ans=0;for(int i=1;i<=q;i+=2)ans+=(win[i]&&win[i+1]);
    printf("%d
",ans);
    for(int i=1;i<=q;i+=2)if(win[i]&&win[i+1])printf("%d
",(i+1)>>1);
    return 0;
}
View Code

 

原文地址:https://www.cnblogs.com/Jessie-/p/10230686.html