SPOJ PROFIT Maximum Profit (最大闭合权子图,最小割)

题目:

http://www.spoj.com/problems/PROFIT/

 

题意:

n个中转站,每个站建立花费Xi

m个客户,每个客户需要中转站Ai,Bi,获得收益为Ci

求最大收益

 

最大闭合权子图(详见《最小割模型在信息学竞赛中的应用》)

闭合图:有向图的点集,集合中的点的出边都指向点集内部的点,$(u,v)\in E$则当$u$成立时$v$成立(即:$u$蕴含$v$($u->v$))。

最大权闭合子图:点权之和最大的闭合图。

建图:每一条有向边变为容量为inf,源S到正权点v($w_v>0$)的边容量$w_v$,负权点v($w_v<0$)到汇$T$的边容量$-w_v$,零权点v($w_v=0$)不与源和汇相连。然后求最小割(SUM-最大流)即为答案。

 

方法:

最大闭合权子图

建图:

S向客户连边(S,i,Ci)
站向T连边(i,T,Xi)
客户向站连边(i,j,inf)
答案为sum-dinic()

 1 void build()
 2 {
 3     dinic.init(n + m + 2);
 4     dinic.st = 0;
 5     dinic.ed = 1;
 6     for (int i = 0; i < n; i++)
 7     {
 8         int x;
 9         scanf("%d", &x);
10         dinic.adde(i + 2, dinic.ed, x);
11     }
12     for (int i = 0 + n + 2; i < m + n + 2; i++)
13     {
14         int u, v, w;
15         scanf("%d%d%d", &u, &v, &w);
16         sum += w;
17         dinic.adde(dinic.st, i, w);
18         dinic.adde(i, u + 1, inf);
19         dinic.adde(i, v + 1, inf);
20     }
21 }

 

代码:

  1 /********************************************
  2 *ACM Solutions
  3 *
  4 *@Title: SPOJ PROFIT Maximum Profit
  5 *@Version: 1.0
  6 *@Time: 2014-08-27
  7 *@Solution: http://www.cnblogs.com/xysmlx/p/xxxxxxx.html
  8 *
  9 *@Author: xysmlx(Lingxiao Ma)
 10 *@Blog: http://www.cnblogs.com/xysmlx
 11 *@EMail: xysmlx@163.com
 12 *
 13 *Copyright (C) 2011-2015 xysmlx(Lingxiao Ma)
 14 ********************************************/
 15 // #pragma comment(linker, "/STACK:102400000,102400000")
 16 #include <cstdio>
 17 #include <iostream>
 18 #include <cstring>
 19 #include <string>
 20 #include <cmath>
 21 #include <set>
 22 #include <list>
 23 #include <map>
 24 #include <iterator>
 25 #include <cstdlib>
 26 #include <vector>
 27 #include <queue>
 28 #include <stack>
 29 #include <algorithm>
 30 #include <functional>
 31 using namespace std;
 32 typedef long long LL;
 33 #define pb push_back
 34 #define ROUND(x) round(x)
 35 #define FLOOR(x) floor(x)
 36 #define CEIL(x) ceil(x)
 37 const int maxn = 60010;
 38 const int maxm = 2000010;
 39 const int inf = 0x3f3f3f3f;
 40 const LL inf64 = 0x3f3f3f3f3f3f3f3fLL;
 41 const double INF = 1e30;
 42 const double eps = 1e-6;
 43 const int P[4] = {0, 0, -1, 1};
 44 const int Q[4] = {1, -1, 0, 0};
 45 const int PP[8] = { -1, -1, -1, 0, 0, 1, 1, 1};
 46 const int QQ[8] = { -1, 0, 1, -1, 1, -1, 0, 1};
 47 
 48 /**
 49 *最大流最小割:加各种优化的Dinic算法($O(V^2E)$)
 50 *输入:图(链式前向星),n(顶点个数,包含源汇),st(源),ed(汇)
 51 *输出:Dinic(NdFlow)(最大流),MinCut()(最小割)(需先求最大流)
 52 *打印路径方法:按反向边(i&1)的flow 找,或者按边的flow找
 53 */
 54 // const int maxn = 0;
 55 // const int maxm = 0;
 56 // const int inf = 0x3f3f3f3f;
 57 struct DINIC
 58 {
 59     struct Edge
 60     {
 61         int u, v;
 62         int cap, flow;
 63         int next;
 64     } edge[maxm];
 65     int head[maxn], en; //需初始化
 66     int n, m, d[maxn], cur[maxn];
 67     int st, ed;
 68     bool vis[maxn];
 69     void init(int _n = 0)
 70     {
 71         n = _n;
 72         memset(head, -1, sizeof(head));
 73         en = 0;
 74     }
 75     void addse(int u, int v, int cap, int flow)
 76     {
 77         edge[en].u = u;
 78         edge[en].v = v;
 79         edge[en].cap = cap;
 80         edge[en].flow = flow;
 81         edge[en].next = head[u];
 82         head[u] = en++;
 83         cur[u] = head[u];
 84     }
 85     void adde(int u, int v, int cap)
 86     {
 87         addse(u, v, cap, 0);
 88         addse(v, u, 0, 0); //注意加反向0 边
 89     }
 90     bool BFS()
 91     {
 92         queue<int> Q;
 93         memset(vis, 0, sizeof(vis));
 94         Q.push(st);
 95         d[st] = 0;
 96         vis[st] = 1;
 97         while (!Q.empty())
 98         {
 99             int u = Q.front();
100             Q.pop();
101             for (int i = head[u]; i != -1; i = edge[i].next)
102             {
103                 int v = edge[i].v;
104                 int w = edge[i].cap - edge[i].flow;
105                 if (w > 0 && !vis[v])
106                 {
107                     vis[v] = 1;
108                     Q.push(v);
109                     d[v] = d[u] + 1;
110                     if (v == ed) return 1;
111                 }
112             }
113         }
114         return false;
115     }
116     int Aug(int u, int a)
117     {
118         if (u == ed) return a;
119         int aug = 0, delta;
120         for (int &i = cur[u]; i != -1; i = edge[i].next)
121         {
122             int v = edge[i].v;
123             int w = edge[i].cap - edge[i].flow;
124             if (w > 0 && d[v] == d[u] + 1)
125             {
126                 delta = Aug(v, min(a, w));
127                 if (delta)
128                 {
129                     edge[i].flow += delta;
130                     edge[i ^ 1].flow -= delta;
131                     aug += delta;
132                     if (!(a -= delta)) break;
133                 }
134             }
135         }
136         if (!aug) d[u] = -1;
137         return aug;
138     }
139     int Dinic(int NdFlow)
140     {
141         int flow = 0;
142         while (BFS())
143         {
144             memcpy(cur, head, sizeof(int) * (n + 1));
145             flow += Aug(st, inf);
146             /*如果超过指定流量就return 掉*/
147             if (NdFlow == inf) continue;
148             if (flow > NdFlow) break;
149         }
150         return flow;
151     }
152     /*残余网络*/
153     void Reduce()
154     {
155         for (int i = 0; i < en; i++) edge[i].cap -= edge[i].flow;
156     }
157     /*清空流量*/
158     void ClearFlow()
159     {
160         for (int i = 0; i < en; i++) edge[i].flow = 0;
161     }
162     /*求最小割*/
163     vector<int> MinCut()
164     {
165         BFS();
166         vector<int> ans;
167         for (int u = 0; u < n; u++)
168         {
169             if (!vis[u]) continue;
170             for (int i = head[u]; i != -1; i = edge[i].next)
171             {
172                 if (i & 1) continue; /*忽略反向边*/
173                 int v = edge[i].v;
174                 int w = edge[i].cap;
175                 if (!vis[v] && w > 0) ans.push_back(i);
176             }
177         }
178         return ans;
179     }
180 } dinic;
181 
182 int kase;
183 int n, m;
184 int sum;
185 void init()
186 {
187     kase++;
188     sum = 0;
189 }
190 void input()
191 {
192     scanf("%d%d", &n, &m);
193 }
194 void debug()
195 {
196     //
197 }
198 void build()
199 {
200     dinic.init(n + m + 2);
201     dinic.st = 0;
202     dinic.ed = 1;
203     for (int i = 0; i < n; i++)
204     {
205         int x;
206         scanf("%d", &x);
207         dinic.adde(i + 2, dinic.ed, x);
208     }
209     for (int i = 0 + n + 2; i < m + n + 2; i++)
210     {
211         int u, v, w;
212         scanf("%d%d%d", &u, &v, &w);
213         sum += w;
214         dinic.adde(dinic.st, i, w);
215         dinic.adde(i, u + 1, inf);
216         dinic.adde(i, v + 1, inf);
217     }
218 }
219 void solve()
220 {
221     build();
222     printf("%d\n", sum - dinic.Dinic(inf));
223 }
224 void output()
225 {
226     //
227 }
228 int main()
229 {
230     // int size = 256 << 20; // 256MB
231     // char *p = (char *)malloc(size) + size;
232     // __asm__("movl %0, %%esp\n" :: "r"(p));
233 
234     // std::ios_base::sync_with_stdio(false);
235 #ifdef xysmlx
236     freopen("in.cpp", "r", stdin);
237 #endif
238 
239     kase = 0;
240     int T;
241     scanf("%d", &T);
242     while (T--)
243     {
244         init();
245         input();
246         solve();
247         output();
248     }
249     return 0;
250 }
SPOJ PROFIT

 

posted @ 2014-08-27 14:29  xysmlx  阅读(258)  评论(0编辑  收藏  举报