uva 1515 Pool construction(最大流)

来源:互联网 发布:java swing jpanel 编辑:程序博客网 时间:2024/05/19 22:58

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)点转换为洞啦。

#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <cstdlib>using namespace std;typedef long long ll;const int PO = 105;const int N = 10005;const int M = 40005;const int INF = 0x3f3f3f3f;int n, m, s, t;int d, f, b, sum;char Gra[PO][PO];struct Dinic{    int ec, head[N], first[N], que[N], lev[N];    int Next[M], to[M], v[M];    void init() {        ec = 0;        memset(first, -1, sizeof(first));    }    void addEdge(int a,int b,int c) {        to[ec] = b;        v[ec] = c;        Next[ec] = first[a];        first[a] = ec++;        to[ec] = a;        v[ec] = 0;        Next[ec] = first[b];        first[b] = ec++;    }    int BFS() {        int kid, now, f = 0, r = 1, i;        memset(lev, 0, sizeof(lev));        que[0] = s, lev[s] = 1;        while (f < r) {            now = que[f++];            for (i = first[now]; i != -1; i = Next[i]) {                kid = to[i];                    if (!lev[kid] && v[i]) {                    lev[kid] = lev[now] + 1;                        if (kid == t) return 1;                    que[r++] = kid;                }            }        }        return 0;    }    int DFS(int now, int sum) {        int kid, flow, rt = 0;        if (now == t || sum == 0) return sum;        for (int i = head[now]; i != -1 && rt < sum; i = Next[i]) {            head[now] = i;              kid = to[i];            if (lev[kid] == lev[now] + 1 && v[i]) {                flow = DFS(kid, min(sum - rt, v[i]));                if (flow) {                    v[i] -= flow;                    v[i^1] += flow;                    rt += flow;                } else lev[kid] = -1;               }                   }        return rt;    }    int dinic() {        int ans = 0;        while (BFS()) {            for (int i = 0; i < N; i++) {                head[i] = first[i];            }                       ans += DFS(s, INF);        }        return ans;    }   }din;void input() {    sum = 0;    scanf("%d %d\n", &n, &m);    scanf("%d %d %d", &d, &f, &b);    s = n * m + 1, t = n * m + 2;    for (int i = 0; i < m; i++) {        getchar();        for (int j = 0; j < n; j++) {            scanf("%c", &Gra[i][j]);            if ((i == 0 || j == 0 || i == m - 1 || j == n - 1) && Gra[i][j] == '.') {                Gra[i][j] = '#';                    sum += f;            }        }       }}void build() {    for (int i = 0; i < m; i++) {        for (int j = 0; j < n; j++) {            if (i != 0) din.addEdge(i * n + j, (i - 1) * n + j, b);            if (j != 0) din.addEdge(i * n + j, i * n + j - 1, b);            if (i != m - 1) din.addEdge(i * n + j, (i + 1) * n + j, b);            if (j != n - 1) din.addEdge(i * n + j, i * n + j + 1, b);        }     }    for (int i = 0; i < m; i++) {        for (int j = 0; j < n; j++) {            if (i == 0 || j == 0 || i == m - 1 || j == n - 1)               din.addEdge(s, i * n + j, INF);              else if (Gra[i][j] == '#') din.addEdge(s, i * n + j, d);                else din.addEdge(i * n + j, t, f);        }       }}int main() {    int T;    scanf("%d", &T);    while (T--) {        din.init();        input();            build();        printf("%d\n", sum + din.dinic());    }    return 0;}
0 0
原创粉丝点击