bzoj 4554: [Tjoi2016&Heoi2016]游戏 (最大流)

题目描述

传送门

题解

对于每行每列以#为界限,分成好几个连通块,同一连通块中的点只能选取一个。
位置都只能属于一个连通块。
s->列的连通块 ,容量为1
行的连通块->T ,容量为1
每个空地从他所属的列连通块->行连通块。
然后求最大流即可。

代码

#include
#include
#include
#include
#include
#include
#define N 100003
#define inf 1000000000
using namespace std;
int belongx[53][53],belongy[53][53],point[N],v[N],nxt[N],remain[N];
int tot,mp[53][53],last[N],num[N],cur[N],deep[N],n,m,sz;
char s[103];
void add(int x,int y,int z)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
int addflow(int s,int t)
{
    int ans=inf; int now=t;
    while (now!=s) {
        ans=min(ans,remain[last[now]]);
        now=v[last[now]^1];
    }
    now=t;
    while (now!=s) {
        remain[last[now]]-=ans;
        remain[last[now]^1]+=ans;
        now=v[last[now]^1];
    }
    return ans;
}
void bfs(int s,int t)
{
    for (int i=1;i<=t;i++) deep[i]=t+1;
    deep[t]=0;
    queue<int> p; p.push(t);
    while (!p.empty()){
        int now=p.front(); p.pop();
        for (int i=point[now];i!=-1;i=nxt[i])
         if (deep[v[i]]==t+1&&remain[i^1])
          deep[v[i]]=deep[now]+1,p.push(v[i]);
    }
}
int isap(int s,int t)
{
    int ans=0; int now=s; bfs(s,t);
    for (int i=1;i<=t;i++) num[deep[i]]++;
    for (int i=1;i<=t;i++) cur[i]=point[i];
    while (deep[s]if (now==t) {
            ans+=addflow(s,t);
            now=s;
        }
        bool pd=false;
        for (int i=cur[now];i!=-1;i=nxt[i])
         if (remain[i]&&deep[now]==deep[v[i]]+1){
            pd=true;
            cur[now]=i;
            last[v[i]]=i;
            now=v[i];
            break;
         }
        if (!pd) {
            int minn=t+1;
            for (int i=point[now];i!=-1;i=nxt[i])
             if (remain[i]) minn=min(minn,deep[v[i]]);
            if (!-num[deep[now]]) break;
            num[deep[now]=minn+1]++;
            cur[now]=point[now];
            if (now!=s) now=v[last[now]^1];
        }
    }
    return ans;
}
int main()
{
    freopen("a.in","r",stdin);
    tot=-1;
    memset(point,-1,sizeof(point));
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) {
        scanf("%s",s+1);
        for (int j=1;j<=m;j++) {
            if (s[j]=='*') mp[i][j]=1;
            if (s[j]=='x') mp[i][j]=2;
            if (s[j]=='#') mp[i][j]=3;
        }
    }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
         if (j==1||mp[i][j-1]==3) ++sz;
         belongx[i][j]=sz;
        }
    int t=sz;
    for (int j=1;j<=m;j++)
        for (int i=1;i<=n;i++){
         if (i==1||mp[i-1][j]==3) ++sz;
         belongy[i][j]=sz;
        }
    int S=sz+1; int T=sz+2;
    for (int i=1;i<=t;i++) add(S,i,1);
    for (int j=t+1;j<=sz;j++) add(j,T,1);
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
      if(mp[i][j]==1) add(belongx[i][j],belongy[i][j],1);
    printf("%d\n",isap(S,T));
} 

你可能感兴趣的:(最大流,网络流)