網路流24題 P2763 試題庫問題
阿新 • • 發佈:2020-08-24
思路
問題模型:二分圖多重匹配
轉化模型:網路最大流
與圓桌問題十分相似,建出二分圖,然後跑網路最大流,如果最大流小於 \(m\),那麼沒有合法方案,否則就從 \(1\sim k\) 列舉題型所連點,如果 \(val\) 為 \(0\) 則表示用過,就可以直接輸出,特別要注意的是輸出方案時容量為 \(0\) 的邊還可能是從起點連過來的邊的反向邊,所以判斷的時候要加上這個條件。下面說一下如何建圖:
- 將源點 \(s\) 與題型 \(1\sim k\) 相連,容量為這種題型需要的題數 \(c_i\)
- 將每個題與匯點 \(t\) 相連,容量為 \(1\),因為每個題只能選一次
- 將每個題型與可以為這個題型的點相連,容量為 \(1\)
按樣例建出來的圖大概是這種感覺(不要問我為什麼長得這麼醜而且是上下的!):
還有一些小細節的實現可以看程式碼。(其實也沒啥細節)
程式碼
/* Name: 試題庫問題 Author: Loceaner Date: 24/08/20 10:34 Debug: 不知道為什麼入度為0的點還輸出了…… update:輸出方案時容量為0的邊可能是從起點連過來的邊的反向邊qwq */ #include <cmath> #include <queue> #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 n, k, s, t, cnt = 1, tot = 0; struct node { int to, nxt, val; } e[A]; int head[A], inq[A], dep[A], c[A], p[A], cur[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 bfs() { queue <int> Q; for (int i = 1; i <= n + k + 2; i++) cur[i] = head[i], inq[i] = 0, dep[i] = inf; dep[s] = 0, Q.push(s), inq[s] = 1; while (!Q.empty()) { int x = Q.front(); Q.pop(), inq[x] = 0; for (int i = head[x]; i; i = e[i].nxt) { int to = e[i].to; if (dep[to] > dep[x] + 1 && e[i].val) { dep[to] = dep[x] + 1; if (!inq[to]) Q.push(to), inq[to] = 1; } } } return dep[t] != inf; } int dfs(int x, int flow) { if (x == t) return flow; int tmp = 0; for (int i = cur[x]; i; i = e[i].nxt) { cur[x] = i; int to = e[i].to; if (dep[to] == dep[x] + 1 && e[i].val) { if (tmp = dfs(to, min(flow, e[i].val))) { e[i].val -= tmp, e[i ^ 1].val += tmp; return tmp; } } } return 0; } int main() { k = read(), n = read(); s = k + n + 1, t = k + n + 2; for (int i = 1; i <= k; i++) { c[i] = read(), tot += c[i]; add(s, i, c[i]), add(i, s, 0); } for (int i = 1; i <= n; i++) { p[i] = read(); add(i + k, t, 1), add(t, i + k, 0); for (int j = 1, x; j <= p[i]; j++) { x = read(); add(x, i + k, 1), add(i + k, x, 0); } } int now = 0, ans = 0; while (bfs()) while (now = dfs(s, inf)) ans += now; if (ans < tot) return puts("No Solution!"), 0; for (int i = 1; i <= k; i++) { cout << i << ": "; for (int j = head[i]; j; j = e[j].nxt) { if (e[j].val == 0 && e[j].to != s) cout << e[j].to - k << " "; //存的時候加了k,所以現在要減去k } puts(""); } }