题解 P2762 【太空飞行计划问题】

loceaner

2020-02-15 10:41:54

Solution

## 思路 > 问题模型:最大权闭合图 > > 转化模型:网络最小割 这道题是网络流中一个比较重要的模型:**最大权闭合图转最大流** 建立超级源点$S$和超级汇点$T$,然后每个实验连一条从$S$到实验,流量为实验收益的边,每个仪器连一条从仪器到$T$, 流量为仪器耗费的边,然后需要的仪器就连一条从实验到仪器流量为$inf$(无穷大)的边,因为实验到仪器的边的流量为正无穷,所以最小割一定不会在上面,根据最大流最小割定理,最大流就等于最小割,我们按照以上所说建图,求出最大流,之后用实验利益的总和减去最大流,得出的就是最大净收益 最大流算法我用的是$\text{Dinic}$算法,因为这样方便输出,为什么?因为如果$d[i]$不为$0$就说明它一定用过,这样就能方便输出啦~ ## 代码 ```cpp #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 1e5 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } int m, n, cnt, opt, S, T, ans, head[A], d[A], q[A]; struct node { int from, to, nxt, val; } e[A]; inline void add(int from, int to, int val) { e[cnt].to = to; e[cnt].val = val; e[cnt].nxt = head[from]; head[from] = cnt++; } inline bool makelevel(int s, int t) { memset(d, 0, sizeof(d)); memset(q, 0, sizeof(q)); int l = 0, r = 0; d[s] = 1; q[r++] = s; while (l < r) { int x = q[l++]; if (x == t) return true; for (int i = head[x]; i != -1; i = e[i].nxt) { int to = e[i].to; if (d[to] == 0 && e[i].val > 0) { d[to] = d[x] + 1; q[r++] = e[i].to; } } } return false; } int dfs(int x, int flow, int t) { if (x == t) return flow; int sum = 0; for (int i = head[x]; i != -1; i = e[i].nxt) { int to = e[i].to; if (d[to] == d[x] + 1 && e[i].val > 0) { int tmp = dfs(to, min(flow - sum, e[i].val), t); e[i].val -= tmp, e[i ^ 1].val += tmp; sum += tmp; if (sum == flow) return sum; } } return sum; } int main() { memset(head, -1, sizeof(head)); m = read(), n = read(); int S = 0, T = 555; int w, tot = 0, x; for (int i = 1; i <= m; i++) { scanf("%d", &w), tot += w; add(S, i, w), add(i, S, 0); while (getchar() == ' ') { scanf("%d", &x); add(i, x + m, inf); add(x + m, i, 0); } } for (int i = 1; i <= n; i++) { x = read(); add(i + m, T, x), add(T, i + m, 0); } while (makelevel(S, T)) ans += dfs(S, inf, T); ans = tot - ans; for (int i = 1; i <= m; i++) if (d[i]) cout << i << ' '; puts(""); for (int i = 1; i <= n; i++) if (d[i + m]) cout << i << ' '; puts(""); cout << ans << '\n'; return 0; } ```