网络流24题 P3254 圆桌问题
感觉做起来很爽,理解题意之后建图跑就行了
思路
问题模型:二分图多重匹配
转化模型:网络最大流
大概是做过最简单的网络流题了……
其实就是一个二分图多重匹配的问题,一个单位可以匹配多个桌子,问题就是求能不能使这些单位的人都能入座且一个桌子上最多只有一个来自同一单位的人。
将源点 \(S\) 与单位之间连流量为代表人数 \(r_i\) 的边,顺便记录总人数 \(tot\),将餐桌和汇点 \(T\) 之间连流量为容纳人数 \(c_i\) 的边,然后将每个单位与每个餐桌之间连流量为 \(1\) 的边,这样就建好了图。
这张图是可以满足题目条件的,因为单位与桌子之间连了流量为 \(1\) 的边,所以一个桌子上最多只有一个来自同一单位的人。
之后在这张图上跑最大流,看看在满足能不能容纳这 \(tot\) 个人,如果最大流小于 \(tot\),就说明没有可行方,如果最大流大于或等于 \(tot\),那就说明可以,枚举每个单位,检查这个单位连着的边的流量是否为 \(0\),为 \(0\) 则表示走最大流的时候走了这条边,即能在这个餐桌上,输出即可,否则就不能。
具体的细节看一看代码。
代码
/*
Name: P3254 圆桌问题
Author: Loceaner
Date: 24/08/20 08:07
*/
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int A = 2e5 + 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, m, s, t, cnt = 1;
struct node { int to, nxt, val; } e[A];
int head[A], dep[A], inq[A], cur[A], r[A], c[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 + m + 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() {
m = read(), n = read();
int jump = n + m, tot = 0;
s = jump + 1, t = jump + 2;
for (int i = 1; i <= m; i++)
r[i] = read(), tot += r[i],
add(s, i, r[i]), add(i, s, 0);
for (int i = 1; i <= n; i++)
c[i] = read(), add(i + m, t, c[i]), add(t, i + m, 0);
for (int i = 1; i <= m; i++)
for (int j = m + 1; j <= m + n; j++)
add(i, j, 1), add(j, i, 0);
int ans = 0, now = 0;
while (bfs()) while (now = dfs(s, inf)) ans += now;
if (ans < tot) return puts("0"), 0;
puts("1");
for (int i = 1; i <= m; i++) {
for (int j = head[i]; j; j = e[j].nxt)
if (e[j].val == 0) cout << e[j].to - m << ' ';
//餐桌从m+1开始编号,所以要减m
puts("");
}
return 0;
}
转载不必联系作者,但请声明出处