uva 1515 Pool construction(最小割)

 

题目大意:给一块n×m的地皮,上面有草地‘#’和洞‘.’ 。草地转换成洞需要d的费用,把洞填上种上草需要f的费用,在草地和洞之间加一道墙需要b的费用。现在问把这块n×m的地皮变成池子的最小费用是多少。(池子:边缘必须都是草地,每块草地与洞之间都有墙隔开)

解题思路:建图方式非常巧妙。先把所有边界上的洞全部变成草地,并记录费用sum。设置超级源点,连向所有边界的草地,容量为INF,然后连向其他草地,容量为d。设置超级汇点,使所有的洞,连向超级汇点,容量为f。然后地皮内每个点都与相邻的点连边,容量为b。

#####
##.##
#.#.#
#####
 

超级源点向(3,3)的#连了一条边,容量为d。如果将d转换为洞的话最终答案ans + 3b - (d + b)。所以要不要对(3,3)进行转换,就变成了3b和(d + b)也就是d 和2b谁大谁小的问题。当d大于2b时,把#转换成洞是不划算的,在最大流中体现为,当d >= 2b时,(3,3)点接受了(4,3)点流入的流,(3,3)点的流量就会化为三段b的流流向(2,3)(3,2)(3,4)三个点(最大流嘛,三条边满流),就相当于在不转化(3,3)点,并在其与(2,3)(3,2)(3,4)三个点间建墙,流量 = 费用 = 3b。当d < 2b时,即使接受了(4,3)点流入的流,(3,3)点的流量还是不够流满(2,3)(3,2)(3,4)三个点,所以最后流向三个点的流量并没有3b,而是本身流量加上(4,3)点流入的流量,流量 = 费用 = d + b,也就是把(3,3)点转换为洞啦。

 

题解转自:http://blog.csdn.net/llx523113241/article/details/48195599

 

代码:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <cctype>
using namespace std;
typedef unsigned long long ULL;

const int maxn = 3000;
const int INF = 0x3f3f3f3f;

bool used[maxn];

int n,m;///n行

struct edge{
    int to,cap,rev;
};
vector <edge> G[maxn];

void addedge(int from,int to,int cap){
    G[from].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){from,0,G[from].size()-1});
}

int dfs(int v,int t,int f){
    if(v==t) return f;
    used[v] = 1;
    for(int i =0;i<G[v].size();++i){
        edge &e = G[v][i];
        if(!used[e.to] &&e.cap>0){
            int d = dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s,int t){
    int flow = 0;
    while(1){
        memset( used,0,sizeof(used));
        int f = dfs(s,t,INF);
        if(f==0) break;
        flow += f;
    }
    return flow;
}

int ans = 0,S,T;
int d,f,b;
int id[maxn][maxn];///节点编号
char g[maxn][maxn];///原图
int dx[4] = {0,0,1,-1};
int dy[4] = {-1,1,0,0};

void init(){
    for(int i = 0;i<maxn;++i){
        G[i].clear();
    }
    ans = 0;

}
///起点为0 终点为n*m+1
void changenum(){
    S = 0;
    T = m*n+1;
    for(int i = 0;i < n;++i){
        for(int j = 0;j < m;++j){
            id[i][j] = i*m+j+1;
        }
    }
}

int main() {

    int t;
	scanf("%d",&t);
	for(int tt = 1; tt<=t;++tt){

        init();

        scanf("%d%d%d%d%d",&m,&n,&d,&f,&b);///m列,变洞d,变草f,围栏b
        for(int i = 0;i < n;++i){
            scanf("%s",g[i]);
        }

        changenum();
        for(int i = 0; i < n; ++i){
            for(int j = 0; j < m; ++j){
                if(j == 0 || j == m-1 || i == 0 || i == n-1){
                    if(g[i][j] == '.') ans += f;
                    addedge(S,id[i][j],INF);///起点向草连边
                }
                else{
                    if(g[i][j] == '.') addedge(id[i][j],T,f);///洞向终点连边
                    else addedge(S,id[i][j],d);///起点向草连边
                }
                ///四个方向互相连边
                for(int k = 0; k < 4; ++k){
                    int ni = i+dx[k],nj = j+dy[k];
                    if(ni<0 || ni>=n || nj<0 || nj>=m) continue;
                    addedge(id[i][j],id[ni][nj],b);
                }
            }
        }
        printf("%d\n",ans+max_flow(S,T));
	}

    return 0;
}
/*
3
3 3
5 5 1
#.#
#.#
###
5 4
1 8 1
#..##
##.##
#.#.#
#####
2 2
27 11 11
#.
.#
*/

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值