2175. 飞行员配对方案问题(最大流之二分图匹配)

本文介绍了在二战时期,如何通过最大流算法中的Dinic算法解决外籍飞行员与英国飞行员的最佳配对问题,以实现最多飞机的派出。文章详细描述了问题背景,输入输出格式以及使用匈牙利算法进行配对的解决方案。
摘要由CSDN通过智能技术生成

2175. 飞行员配对方案问题 - AcWing题库

第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。

由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值