问题模型: 二分图多重匹配
转化模型 :最大流
我是来这里认罪的!
我没加边跑网络流!我加了个自环!我 Dinic 模板还打错了!
你们快来嘲讽一下我!我是罪人!还连累小 ly!
建图
建立二分图,每个单位为 X
集合中的顶点,每个餐桌为 Y
集合中的顶点,增设附加源 S
和汇 T
。
1、从 S
向每个 Xi
顶点连接一条容量为该单位人数的有向边。
2、从每个 Yi
顶点向 T
连接一条容量为该餐桌容量的有向边。
3、X
集合中每个顶点向 Y
集合中每个顶点连接一条容量为 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 m, n, 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 sum = 0;
scanf("%d%d", &m, &n);
s = 0, t = m + n + 1;
for (int i = 1; i <= m; i ++)
for (int j = 1; j <= n; j ++)
add(i, j + m, 1);
for (int i = 1; i <= m; i ++) {
int r;
scanf("%d", &r);
sum += r;
add(s, i, r);
}
for (int i = 1; i <= n; i ++) {
int c;
scanf("%d", &c);
add(m + i, t, c);
}
int ans = Dinic();
if (ans != sum) printf("0\n");
else {
printf("1\n");
for (int u = 1; u <= m; 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 - m);
}
printf("\n");
}
}
return 0;
}