HDU3879:Base Station
——建通讯站。每个通讯站有一定耗费,两个特定的通讯站之间建立通讯以后会有一定收益,问怎样建立通讯站可以使得收益最大
——最大权闭合子图-》最小割
——url:http://acm.hdu.edu.cn/showproblem.php?pid=3879
——————————————————————————————————————————
首先考虑将图转化。
即一条通讯线路有一定收益,但需要建立两个通讯站,这两个通讯站有一定造价。
将通讯线路也变成点,点权为收益,连两条有向只向两个通讯站。
原来的通讯站点权不变。
由此,题目转化为在新的图中求一个闭合图,使得其点权最大,即最大权闭合子图
(闭合图个人理解的定义:对于点集中的所有点,其后继结点一定在该点集中)
——————————————————————————————————————————————
最大权闭合子图的求解参见IOI2007胡伯涛的论文。
新建一个源点S,S与每个通讯线路的点相连,边权为收益
新建一个汇点T,T与每个通讯站相连,边权为建造费用
通讯站与通讯线路之间的边权为无穷大
最后的结果为所有通讯线路总的收益-最小割
#include<stdio.h> #include<memory.h> #define N 56000 #define oo 0x7ffffff int head[N], nxt[N * 10], ev[N*10],ew[N*10]; int pi[N], pe[N]; int CurrentNode[N]; int queue[N]; int d[N]; int numbs[N]; int n, m, node_sum, source, sink, ans, num; void addedge(int u, int v, int w) { nxt[++num] = head[u]; head[u] = num; ev[num] = v; ew[num] = w; nxt[++num] = head[v]; head[v] = num; ev[num] = u; ew[num] = 0; } void readin() { int i, a, b, c; num = -1; memset(head, -1, sizeof(head)); memset(nxt, -1, sizeof(nxt)); for (i = 1; i <= n; i++) { scanf("%d", &c); addedge(i, n + m + 2, c); } node_sum = n; ans = 0; for (i = 0; i < m; i++) { node_sum++; scanf("%d%d%d", &a, &b, &c); addedge(node_sum, a, oo); addedge(node_sum, b, oo); addedge(n + m + 1, node_sum, c); ans += c; } source = n + m + 1; sink = n + m + 2; node_sum = n + m + 2; } int Augment(int source, int sink) { int i, j, tmp, width(oo); for (i = sink, j = pi[i]; i != source; i = j, j = pi[j]) { tmp = ew[pe[i]]; if (tmp < width) width = tmp; } for (i = sink, j = pi[i]; i != source; i = j, j = pi[j]) { ew[pe[i]] -= width; ew[pe[i] ^ 1] += width; } return width; } int Retreat(int &i, int n, int source, int sink) { int tmp; int j, mind(n - 1); for (j = head[i]; j != -1; j = nxt[j]) if (ew[j] > 0 && d[ev[j]] < mind) mind = d[ev[j]]; tmp = d[i]; numbs[d[i]]--; d[i] = 1 + mind; numbs[d[i]]++; if (i != source) i = pi[i]; return numbs[tmp]; } int find_max_flow(int n, int source, int sink) { int flow(0), i, j; memset(d, 0, sizeof(d)); memset(numbs, 0, sizeof(numbs)); numbs[0] = n; for (i = 1; i <= n; i++) CurrentNode[i] = head[i]; i = source; for (; d[source] < n;) { for (j = CurrentNode[i]; j != -1; j = nxt[j]) if (ew[j] > 0 && d[i] == d[ev[j]] + 1) break; if (j != -1) { CurrentNode[i] = j; pi[ev[j]] = i; pe[ev[j]] = j; i = ev[j]; if (i == sink) { flow += Augment(source, sink); i = source; } } else { CurrentNode[i] = head[i]; if (Retreat(i, n, source, sink) == 0) break; } } return flow; } int main() { while (scanf("%d%d", &n, &m) != EOF) { readin(); ans -= find_max_flow(node_sum, source, sink); printf("%d\n", ans); } return 0; }