uva 1515 Pool construction(最小割)

来源:互联网 发布:linux daemon命令 编辑:程序博客网 时间:2024/05/29 18:22

题目大意:给一块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+1void 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;}/*33 35 5 1#.##.####5 41 8 1#..####.###.#.######2 227 11 11#..#*/

1 0