第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。
由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的 2 名飞行员,其中 1 名是英国飞行员,另 1 名是外籍飞行员。
在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。
如何选择配对飞行的飞行员才能使一次派出最多的飞机。
对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。
输入格式
第 1 行有 2 个正整数 m 和 n。m 是外籍飞行员数;n 是皇家空军的飞行员总数。
外籍飞行员编号为 1∼m;英国飞行员编号为 m+1∼n。
接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。
文件最后以 2 个 −1 结束。
输出格式
第 11 行是最佳飞行员配对方案一次能派出的最多的飞机数 M。
接下来 M 行是最佳飞行员配对方案。
每行有 2 个正整数 i 和 j,表示在最佳飞行员配对方案中,外籍飞行员 i 和英国飞行员 j 配对。
如果有多种配对方案,则输出任意一种即可,方案内部配对输出顺序随意。
数据范围
1<m<n<100
输入样例:
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1
输出样例:
4
1 7
2 9
3 8
5 10
解析:
仔细观察匈牙利算法不难发现,匈牙利算法实际上就是一种非常特别的 EK 算法,他每次循环之寻找一条增广路。
因此我们可以用最大流算法来处理最大匹配问题,这里我们使用 Dinic 算法进行处理,时间复杂度从 O(nm)->O(m*sqrt(n))
最大流问题的建图:
建立一个虚拟源点,从源点向每个外籍飞行员连一条容量为1的边;建立一个汇点,每个英国飞行员向连汇点一条容量为1的边。若某个外籍飞行员能与某个英国飞行员搭档,则连一条容量为1的边。
检验原问题解的集合与可行流的集合一一对应:
角度一:原问题的每一个解对应一个可行流
角度二:每一个可行流对应原问题的每一个解
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
using namespace std;
const int N = 1e2 + 5, M = 5205, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c) {
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}
int bfs() {
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i]) {
int ver = e[i];
if (d[ver]==-1&&f[i]) {
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) {
return 1;
}
q[++tt] = ver;
}
}
}
return 0;
}
int find(int u,int limit) {
//cout << "LLLLLLLLLLLLLLLLLLLLLLLL " << endl;
if (u == T)return limit;
int flow = 0;
for (int i = cur[u]; i != -1 && flow < limit; i = ne[i]) {
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i]) {
int t = find(ver, min(f[i], limit - flow));
if (!t)d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int Dinic() {
int ret = 0, flow;
while (bfs()) {
//cout << "PPPPPPPPPPPPPPPPPPPPPPPP " << endl;
while (flow = find(S, INF))ret += flow;
}
return ret;
}
int main() {
scanf("%d%d", &m, &n);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i++) {
add(S, i, 1);
}
for (int i = m + 1; i <= n; i++) {
add(i, T, 1);
}
int a, b;
while (scanf("%d%d", &a, &b) != EOF) {
if (a == -1 && b == -1)break;
add(a, b, 1);
}
//cout << "KKKKKKKKKKKKKKKKKKKKKKKKKK <" << endl;
printf("%d\n", Dinic());
for (int i = 0; i < idx; i += 2) {
if (e[i] > m && e[i] <= n && !f[i]) {
printf("%d %d\n", e[i ^ 1], e[i]);
}
}
return 0;
}