【AT2568】Lotus Leaves

题意

链接:AT2568

一个池塘,有起点叶子、终点叶子和叶子。
同行同列的叶子联通,问最少删去多少叶子(除起点终点)能使得起点和终点不连通。

解法:网络流

题目显然要求图的最小割。(暴力建边带走)

更优解?

将每行、每列各缩为一点,然后对于一片叶子 ( i , j ) ,从行 i 向列 j 连双向边,从超源向它所在的行与列连单向边,从超汇所在行与列向其连单向边,完成建模。(注意无解的特判)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>

#define EXIT {puts("-1");return 0;}

using namespace std;

struct edge{
    int v,c;
    edge():v(0),c(0){}
    edge(int w,int d):v(w),c(d){}
};

vector<edge> vec;
vector<int> point[301];
int d[301],n,m,S,T;
char s[202][202];
queue<int> q;

inline void addedge(int x,int y,int c){point[x].push_back(vec.size()),vec.push_back(edge(y,c));point[y].push_back(vec.size()),vec.push_back(edge(x,0));}

bool bfs(){
    memset(d,0,sizeof(d));
    q.push(S);
    d[S]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i:point[u]){
            edge e=vec[i];int v=e.v,c=e.c;
            if(!d[v]&&c)d[v]=d[u]+1,q.push(v);
        }
    }
    return d[T];
}

int dfs(int u,int flow){
    if(u==T)return flow;
    for(int i:point[u]){
        edge e=vec[i];int v=e.v,c=e.c,fl;
        if(d[v]==d[u]+1&&c&&(fl=dfs(v,min(c,flow)))){vec[i].c-=fl,vec[i^1].c+=fl;return fl;}
    }
    return 0;
}

int main(){
    scanf("%d%d",&n,&m);
    T=(S=n+m+1)+1;
    for(int i=1;i<=n;++i){
        scanf("%s",s[i]+1);
        for(int j=1;j<=m;++j){
            if(s[i][j]=='o')addedge(i+m,j,1),addedge(j,i+m,1);
            if(s[i][j]=='S')addedge(S,i+m,2147483647),addedge(S,j,2147483647);
            if(s[i][j]=='T')addedge(i+m,T,2147483647),addedge(j,T,2147483647);
        }
    }
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(s[i][j]=='S'){for(int k=1;k<=n;++k)if(s[k][j]=='T')EXIT;for(int k=1;k<=m;++k)if(s[i][k]=='T')EXIT;}
    int ans=0;
    while(bfs()){int flow;while(flow=dfs(S,2147483647))ans+=flow;}
    printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/ezoixx174/article/details/81572099