网络流24题 P2763 试题库问题
思路
问题模型:二分图多重匹配
转化模型:网络最大流
与圆桌问题十分相似,建出二分图,然后跑网络最大流,如果最大流小于 \(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("");
}
}
转载不必联系作者,但请声明出处