UVA 1515 Pool construction(最小割)

题目链接
分析:紫书上的一道例题,大意就是让你用最小的花费把草和洞分割开。为了让所有的草以及洞连通,我们引入源点S和汇点T,与S相连的都是草,与T相连的都是洞,草和洞是可以相互转化的,所以把草向S连一条容量为d的弧,表示要让草转化成洞需要割掉这条弧,T的也同理。但是我们还要安放栅栏在草和洞之间,所以我们每个结点要向四周相邻结点连一条容量为f的弧,表示如果割掉这一条弧需要支付f的费用,相当于在两个结点之间安放一个栅栏,画画图就可以理解。

#include<bits/stdc++.h>
#define PII pair<int,int>
#define x first
#define y second
#define MAIN main
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=3e3+10;
struct edge
{
    int u,v,cap,flow;
    edge(int u=0,int v=0,int cap=0,int flow=0):u(u),v(v),cap(cap),flow(flow){}
};
vector<edge>E;
vector<int>g[N];
void add(int u,int v,int cap)
{
    E.push_back(edge(u,v,cap,0));
    E.push_back(edge(v,u,0,0));
    int m=E.size();
    g[u].push_back(m-2);
    g[v].push_back(m-1);
}
int n,m;
int d[N],cur[N],vis[N];
int bfs(int s,int t)
{
    memset(vis,0,sizeof(vis));
    queue<int>q;
    vis[s]=1;d[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<(int)g[u].size();i++){
            edge &e=E[g[u][i]];
            int v=e.v;
            if(!vis[v]&&e.cap>e.flow)
            {
                vis[v]=1;
                q.push(v);
                d[v]=d[u]+1;
            }
        }
    }
    return vis[t];
}
int dfs(int s,int t,int a)
{
    if(s==t||a==0) return a;
    int flow=0,f;
    for(int &i=cur[s];i<(int)g[s].size();i++){
        edge &e=E[g[s][i]];
        int v=e.v;
        if(d[v]==d[s]+1&&(f=dfs(v,t,min(a,e.cap-e.flow)))>0){
            flow+=f;
            e.flow+=f;
            E[g[s][i]^1].flow-=f;
            a-=f;
            if(a==0) break;
        }
    }
    return flow;
}
int max_flow(int s,int t)
{
    int flow=0;
    while(bfs(s,t)){
        memset(cur,0,sizeof(cur));
        flow+=dfs(s,t,INF);
    }
    return flow;
}
int num(int x,int y){return m*(x-1)+y;}
bool judge(int x,int y)
{
    if(x>=1&&x<=n&&y>=1&&y<=m) return true;
    return false;
}
char graph[51][51];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,-1,1};
int MAIN()
{
    int test;
    scanf("%d",&test);
    while(test--)
    {
        scanf("%d%d",&m,&n);
        int dd,f,b;
        E.clear();
        for(int i=0;i<=n*m+1;i++) g[i].clear();
        int s=0,t=n*m+1,ans=0;
        scanf("%d%d%d",&dd,&f,&b);
        for(int i=1;i<=n;i++) scanf("%s",(graph[i]+1));
        for(int i=1;i<=n;i++){
            if(i==1||i==n){
                for(int j=2;j<m;j++){
                    if(graph[i][j]=='.') ans+=f;
                    graph[i][j]='#';
                    add(s,num(i,j),INF);
                }
            }
            else {
                for(int j=2;j<m;j++){
                    if(graph[i][j]=='.') add(num(i,j),t,f);
                    else add(s,num(i,j),dd);
                }
            }
            if(graph[i][1]=='.') ans+=f;
            if(graph[i][m]=='.') ans+=f;
            graph[i][1]='#';graph[i][m]='#';
            add(s,num(i,1),INF);add(s,num(i,m),INF);
            for(int j=1;j<=m;j++){
                for(int k=0;k<4;k++){
                    int nx=i+dx[k];
                    int ny=j+dy[k];
                    if(judge(nx,ny)){
                        add(num(i,j),num(nx,ny),b);
                    }
                }
            }
        }
        ans+=max_flow(s,t);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值