题目:
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 }
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }