UVa 1515 - Pool construction(最小割)

链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4261

 

题意:

输入一个h行w列的字符矩阵,草地用“#”表示,洞用“.”表示。
你可以把草改成洞,每格花费为d,也可以把洞填上草,每格花费为f。
最后还需要在草和洞之间修围栏,每条边的花费为b。
整个矩阵第一行/列和最后一行/列必须都是草。
求最小花费。2≤w,h≤50,1≤d, f, b≤10000。

 

分析:

围栏的作用是把草和洞隔开,让人联想到了“割”这个概念。
可是“割”只是把图中的结点分成了两个部分,而本题中,草和洞都能有多个连通块。怎么办呢?
添加源点S和汇点T,与其他点相连,则所有本不连通的草地/洞就能通过源点和汇点间接连起来了。
由于草和洞可以相互转换,而且转换还需要费用,所以需要一并在“割”中体现出来。
为此,规定与S连通的都是草,与T连通的都是洞,
则S需要往所有草格子连一条容量为d的边,表示必须把这条弧切断(割的容量增加d),
这个格子才能“叛逃”到T的“阵营”,成为洞。
由于题目说明了最外圈的草不能改成洞,从S到这些草格子的边容量应为正无穷
(在这之前需要把边界上的所有洞填成草,累加出这一步所需的费用)。
同理,所有不在边界上的洞格子往T连一条弧,费用为f,
表示必须把这条弧切断(割的容量增加f),才能让这个洞变成草。
相邻两个格子u和v之间需要连两条边u->v和v->u,容量均为b,
表示如果u是草,v是洞,则需要切断弧u->v;如果v是草,u是洞,则需要切断弧v->u。
这样,用最大流算法求出最小割,就可以得到本题的最小花费。

 

代码:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <queue>
  4 #include <vector>
  5 using namespace std;
  6 
  7 /// 结点下标从0开始,注意maxn
  8 struct Dinic {
  9     static const int maxn = 50 * 50 + 5;
 10     static const int INF = 0x3f3f3f3f;
 11     struct Edge {
 12         int from, to, cap, flow;
 13     };
 14 
 15     int n, m, s, t; // 结点数,边数(包括反向弧),源点编号和汇点编号
 16     vector<Edge> edges; // 边表。edges[e]和edges[e^1]互为反向弧
 17     vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
 18     bool vis[maxn]; // BFS使用
 19     int d[maxn]; // 从起点到i的距离
 20     int cur[maxn]; // 当前弧下标
 21 
 22     void init(int n) {
 23         this->n = n;
 24         edges.clear();
 25         for(int i = 0; i < n; i++) G[i].clear();
 26     }
 27     void AddEdge(int from, int to, int cap) {
 28         edges.push_back((Edge){from, to, cap, 0});
 29         edges.push_back((Edge){to, from, 0, 0});
 30         m = edges.size();
 31         G[from].push_back(m-2);
 32         G[to].push_back(m-1);
 33     }
 34     bool BFS() {
 35         memset(vis, 0, sizeof(vis));
 36         queue<int> Q;
 37         Q.push(s);
 38         vis[s] = 1;
 39         d[s] = 0;
 40         while(!Q.empty()) {
 41             int x = Q.front();  Q.pop();
 42             for(int i = 0; i < G[x].size(); i++) {
 43                 Edge& e = edges[G[x][i]];
 44                 if(!vis[e.to] && e.cap > e.flow) { // 只考虑残量网络中的弧
 45                     vis[e.to] = 1;
 46                     d[e.to] = d[x] + 1;
 47                     Q.push(e.to);
 48                 }
 49             }
 50         }
 51         return vis[t];
 52     }
 53     int DFS(int x, int a) {
 54         if(x == t || a == 0) return a;
 55         int flow = 0, f;
 56         for(int& i = cur[x]; i < G[x].size(); i++) { // 从上次考虑的弧
 57             Edge& e = edges[G[x][i]];
 58             if(d[x]+1 == d[e.to] && (f=DFS(e.to, min(a, e.cap-e.flow))) > 0) {
 59                 e.flow += f;
 60                 edges[G[x][i]^1].flow -= f;
 61                 flow += f;
 62                 a -= f;
 63                 if(a == 0) break;
 64             }
 65         }
 66         return flow;
 67     }
 68     int Maxflow(int s, int t) {
 69         this->s = s;  this->t = t;
 70         int flow = 0;
 71         while(BFS()) {
 72             memset(cur, 0, sizeof(cur));
 73             flow += DFS(s, INF);
 74         }
 75         return flow;
 76     }
 77     vector<int> Mincut() { // 在Maxflow之后调用
 78         vector<int> ans;
 79         for(int i = 0; i < edges.size(); i++) {
 80             Edge& e = edges[i];
 81             if(vis[e.from] && !vis[e.to] && e.cap > 0) ans.push_back(i);
 82         }
 83         return ans;
 84     }
 85 } dc;
 86 
 87 const int INF = 0x3f3f3f3f;
 88 const int UP = 50 + 5;
 89 int r, c;
 90 char site[UP][UP];
 91 inline int id(int rr, int cc) { return rr*c+cc; }
 92 
 93 int main() {
 94     int T, d, f, b;
 95     scanf("%d", &T);
 96     while(T--) {
 97         scanf("%d%d%d%d%d", &c, &r, &d, &f, &b);
 98         for(int i = 0; i < r; i++) scanf("%s", site[i]);
 99         int ans = 0;
100         for(int i = 0; i < r; i++) {
101             if(site[i][0] == '.') site[i][0] = '#', ans += f;
102             if(site[i][c-1] == '.') site[i][c-1] = '#', ans += f;
103         }
104         for(int i = 0; i < c; i++) {
105             if(site[0][i] == '.') site[0][i] = '#', ans += f;
106             if(site[r-1][i] == '.') site[r-1][i] = '#', ans += f;
107         }
108 
109         dc.init(r*c+2);
110         int start = r*c, finish = r*c+1;
111         for(int rr = 0; rr < r; rr++) {
112             for(int cc = 0; cc < c; cc++) {
113                 if(site[rr][cc] == '#') {
114                     int cap = (rr==0 || rr==r-1 || cc==0 || cc==c-1) ? INF : d;
115                     dc.AddEdge(start, id(rr,cc), cap);
116                 } else dc.AddEdge(id(rr,cc), finish, f);
117                 if(rr > 0) dc.AddEdge(id(rr,cc), id(rr-1,cc), b);
118                 if(rr < r-1) dc.AddEdge(id(rr,cc), id(rr+1,cc), b);
119                 if(cc > 0) dc.AddEdge(id(rr,cc), id(rr,cc-1), b);
120                 if(cc < c-1) dc.AddEdge(id(rr,cc), id(rr,cc+1), b);
121             }
122         }
123         printf("%d\n", ans + dc.Maxflow(start, finish));
124     }
125     return 0;
126 }

 

转载于:https://www.cnblogs.com/hkxy125/p/9548236.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值