最大权闭合子图:
- 选择一些点,并且这些点出边所指向的点也必须被选择,一些点有收益,一些点有代价,最大化所选权值的收益-代价。
方法:
- 源点S向所有有收益的点连流量为收益的边
- 有代价的点向汇点T连流量为代价的边
- 所有点向所有必须选择的点连流量为inf的边
- 答案是所有收益的和减去最小割
证明:
- 统计出收益和,割边时选择收益和代价中较小的减去,若收益小于代价,就放弃这个收益,若收益大于代价,就减去代价。
该题会出现环,环上的点不能选,逆拓扑排序去除。
1 #include <cstdio> 2 #include <string> 3 #include <vector> 4 #include <queue> 5 6 const int N = 1000, INF = 0x3f3f3f3f; 7 8 int read() { 9 int x = 0, f = 1; 10 char c = getchar(); 11 while (!isdigit(c)) { 12 if (c == ‘-‘) f = -1; 13 c = getchar(); 14 } 15 while (isdigit(c)) { 16 x = (x << 3) + (x << 1) + (c ^ 48); 17 c = getchar(); 18 } 19 return x * f; 20 } 21 22 int min(int x, int y) { 23 if (x <= y) return x; return y; 24 } 25 26 struct Edge { 27 int from, to, flow, cap; 28 }; 29 std::vector<Edge> edges; 30 std::vector<int> G[N]; 31 32 int p[N], d[N], S, T, n, outdgr[N], w[N]; 33 34 void addEdge(int from, int to, int cap) { 35 edges.push_back((Edge){from, to, 0, cap}); 36 edges.push_back((Edge){to, from, 0, 0}); 37 int size = edges.size(); 38 G[from].push_back(size - 2); 39 G[to].push_back(size - 1); 40 } 41 42 void bfs() { 43 bool vis[N] = {}; 44 std::queue<int> Q; Q.push(T); 45 for (int i = 0; i < n; ++ i) d[i] = n; 46 d[T] = 0, vis[T] = 1; 47 while (!Q.empty()) { 48 int u = Q.front(); Q.pop(); 49 int size = G[u].size(); 50 for (int i = 0; i < size; ++ i) { 51 Edge &e = edges[G[u][i]], &g = edges[G[u][i]^1]; 52 if (!vis[e.to] && g.cap > g.flow) { 53 d[e.to] = d[u] + 1; 54 vis[e.to] = 1; 55 Q.push(e.to); 56 } 57 } 58 } 59 } 60 61 int augment() { 62 int x = T, a = INF; 63 while (x != S) { 64 a = min(a, edges[p[x]].cap - edges[p[x]].flow); 65 x = edges[p[x]].from; 66 } 67 x = T; 68 while (x != S) { 69 edges[p[x]].flow += a, edges[p[x]^1].flow -= a; 70 x = edges[p[x]].from; 71 } 72 return a; 73 } 74 75 int maxflow() { 76 int num[N] = {}, cur[N] = {}; 77 bfs(); 78 for (int i = 0; i < n; ++ i) ++ num[d[i]]; 79 int x = S, flow = 0; 80 while (d[S] < n) { 81 if (x == T) flow += augment(), x = S; 82 int size = G[x].size(); bool ok = false; 83 for (int i = cur[x]; i < size; ++ i) { 84 Edge &e = edges[G[x][i]]; 85 if (e.cap > e.flow && d[e.to] + 1 == d[x]) { 86 p[e.to] = G[x][i], cur[x] = i, ok = true, x = e.to; 87 break; 88 } 89 } 90 if (!ok) { 91 int size = G[x].size(), m = n - 1; 92 for (int i = 0; i < size; ++ i) { 93 Edge &e = edges[G[x][i]]; 94 if (e.cap > e.flow) m = min(m, d[e.to]); 95 } 96 if (--num[d[x]] == 0) break; 97 ++num[d[x] = m + 1], cur[x] = 0; 98 if (x != S) x = edges[p[x]].from; 99 } 100 } 101 return flow; 102 } 103 104 int main() { 105 n = read(); int m = read(), sum = 0; S = 0, T = n * m + 1; 106 for (int i = 1; i <= n * m; ++ i) { 107 w[i] = read(); int k = read(); 108 if (i % m) { 109 addEdge(i, i + 1, INF); 110 ++outdgr[i]; 111 } 112 for (int j = 1; j <= k; ++ j) { 113 int x = read(), y = read(); 114 addEdge(x * m + y + 1, i, INF); 115 ++outdgr[x * m + y + 1]; 116 } 117 } 118 std::queue<int> Q; 119 for (int i = 1; i <= n * m; ++ i) { 120 if (outdgr[i] == 0) Q.push(i); 121 } 122 while (!Q.empty()) { 123 int u = Q.front(); Q.pop(); 124 int size = G[u].size(); 125 for (int i = 0; i < size; ++ i) { 126 int v = edges[G[u][i]].to; 127 if (outdgr[v] && --outdgr[v] == 0) Q.push(v); 128 } 129 } 130 for (int i = 1; i <= n * m; ++ i) { 131 if (outdgr[i] == 0) { 132 if (w[i] >= 0) sum += w[i], addEdge(S, i, w[i]); 133 else addEdge(i, T, -w[i]); 134 } 135 } n = n * m + 2; 136 printf("%d\n", sum - maxflow()); 137 return 0; 138 }