问题模型: 二分图多重匹配
转化模型 :最大流
建图
建立二分图,每个类别为 X
集合中的顶点,每个题为 Y
集合中的顶点,增设附加源 S
和汇 T
。
- 从
S
向每个Xi
连接一条容量为该类别所需数量的有向边。 - 从每个
Yi
向T
连接一条容量为1
的有向边。(因为每个题只能被选一次) - 如果一个题
i
属于一个类别j,连接一条从Xj
到Yi
容量为1
的有向边。
求网络最大流,如果最大流量等于所有类别所需之和,则存在解,否则无解。对于每个类别,从 X
集合对应点出发的所有满流边,指向的 Y
集合中的顶点就是该类别的所选的题(一个可行解)。
–
二分图多重匹配问题。X
,Y
集合之间的边容量全部是 1
,保证两个点只能匹配一次,源汇的连边限制了每个点匹配的个数。求出网络最大流,如果流量等于 X
集合所有点与 S
边容量之和,那么则说明 X
集合每个点都有完备的多重匹配。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, inf = 0x7fffffff;
struct Edge {
int next, to, c;
}e[N << 1];
int n, k, s, t;
int cnt = 1;
int head[N], cur[N];
void add(int u, int v, int c) {
e[++ cnt].to = v; e[cnt].c = c; e[cnt].next = head[u]; head[u] = cnt;
e[++ cnt].to = u; e[cnt].c = 0; e[cnt].next = head[v]; head[v] = cnt;
}
int dep[N];
bool bfs(int x) {
queue<int> q;
memset(dep, 0, sizeof(dep));
dep[x] = 1; q.push(x);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (!dep[v] && e[i].c) { dep[v] = dep[u] + 1; q.push(v); }
}
}
if (!dep[t]) return 0;
return 1;
}
int dfs(int u, int flow) {
if (u == t) return flow;
for (int &i = cur[u]; i; i = e[i].next) {
int v = e[i].to;
if (dep[v] == dep[u] + 1 && e[i].c) {
int nowflow = dfs(v, min(flow, e[i].c));
if (nowflow > 0) {
e[i].c -= nowflow;
e[i ^ 1].c += nowflow;
return nowflow;
}
}
}
return 0;
}
int Dinic() {
int res = 0;
while(bfs(s)) {
for (int i = s; i <= t; i ++) cur[i] = head[i];
while(int d = dfs(s, inf)) res += d;
}
return res;
}
int main() {
int m = 0;
scanf("%d%d", &k, &n);
s = 0;
for (int i = 1; i <= k; i ++) {
int r;
scanf("%d", &r);
add(s, i, r);
m += r;
}
t = k + n + 1;
for (int i = 1; i <= n; i ++) {
int p;
scanf("%d", &p);
for (int j = 1; j <= p; j ++) {
int c;
scanf("%d", &c);
add(c, k + i, 1);
}
add(k + i, t, 1);
}
int ans = Dinic();
if (ans != m) printf("No Solution!\n");
else {
for (int u = 1; u <= k; u ++) {
printf("%d: ", u);
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (!e[i].c && v != s) printf("%d ", v - k);
}
printf("\n");
}
}
return 0;
}