【做题笔记】网络流24题
Part1.飞行员配对方案问题
Problem
有两个集合 \(A\),\(B\)。给定正整数 \(n\),\(m\)。\(A = \{x|1\leq x \leq m\}\),\(B = \{y|m+1\leq y \leq n\}\)。
现在要将 \(A\) 与 \(B\) 集合的元素一一配对,有若干个配对关系,形如“\(u\), \(v\) 可凑一对”。
求有多少个元素能配成一对,并求出方案。
Solve
裸的二分图,但是网络流来写。明明匈牙利可以过,但这是网络流的题,所以用网络流做。
配对关系可以转换成连边关系,如果 \(u\) 和 \(v\) 可以配对,就将 \(u \to v\),容量为 \(1\)。
创建两个点,源点 \(s\),汇点 \(t\)。然后将 \(s\) 连向集合 \(A\) 的所有点,集合 \(B\) 的所有点连向 \(t\),容量为 \(1\)。在用此图跑最大流即可。汇点的最大流即为配对的个数。
由于要求方案,要在 \(EK\) 增广的时候记录所配对的点。因为一个增广路上的除去源点和汇点所有点,都会配对。
记得建反边!!
Code
#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
#define inf LLONG_MAX
using namespace std;
inline int read() {
rint x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9){print(x/10);putchar(x%10+'0');}
else putchar(x+'0');
return;
}
const int N = 1e5 + 10;
struct Node {
int v, w, nx;
} e[N << 1];
int n, m, s = 0, t = 114, tot = 1, h[N], w[N], ansn, last[N], ans[N];
void add(int u, int v, int w) {
e[++tot].v = v;
e[tot].w = w;
e[tot].nx = h[u];
h[u] = tot;
}
int bfs() {
memset(last, -1, sizeof last);
queue<int> q;
q.push(s);
w[s] = inf;
while(!q.empty()) {
int x = q.front();
q.pop();
if(x == t) break;
for (int i = h[x]; i; i = e[i].nx) {
int y = e[i].v, w_ = e[i].w;
if(w_ > 0 && last[y] == -1) {
w[y] = min(w[x], w_);
last[y] = i;
q.push(y);
}
}
}
return last[t] != -1;
}
int EK() {
int res = 0;
while(bfs()) {
res += w[t];
for (int i = t; i != s; i = e[last[i] ^ 1].v) {
ans[e[last[i] ^ 1].v] = i;
e[last[i]].w -= w[t];
e[last[i] ^ 1].w += w[t];
}
}
return res;
}
signed main() {
m = read(), n = read();
For(i,1,m) add(s, i, 1), add(i, s, 0);
For(i,m+1,n) add(i, t, 1), add(t, i, 0);
while(1) {
int u = read(), v = read();
if(u == -1 && v == -1) break;
add(u, v, 1);
add(v, u, 0);
}
ansn = EK();
cout << ansn << '\n';
For(i,1,m) {
if(!ans[i] || ans[i] == 114) continue;
cout << ans[i] << ' ' << i << '\n';
}
return 0;
}