飞行员配对方案问题------最大流二分图匹配

网络流纯爱战士揍扁了匈牙利牛头人
原题
核心思想就是创建一个超级源点和超级汇点,超级源点连接外籍飞行员,超级汇点连接嘤国飞行员,把能配合的两个人作为一个连接两种飞行员的一个边.能同时起飞的数量就是S点到T点的最大流了

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 10010, M = 200010, inf = 1e8;

int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
//h 表头 e边的终点 f边的权值
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 ++ ;
}

bool 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; i = ne[i]) {
			int ver = e[i];
			if (d[ver] == -1 && f[i]) {
				d[ver] = d[t] + 1;//ver是t的下一级,防止回流
				cur[ver] = h[ver];
				if (ver == T)
					return true;
				q[ ++ tt] = ver;
			}
		}
	}
	return false;
}

int find(int u, int limit) {
	if (u == T)
		return limit;//到了最终点,不用考虑后面的,上限就是可扩展的量
	int flow = 0;
	for (int i = cur[u]; ~i && 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 r = 0, flow;
	while (bfs())//用bfs划分阶级
		while (flow = find(S, inf))//不断用dfs进行扩展
			r += flow;
	return r;
}



int main() {
	cin >> m >> n ;
	//创建一个超级源点S和一个超级汇点T,将问题转化为从
	//s开始,T结束的网络流
	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 (cin >> a >> b, a != -1) {
		add(a, b, 1);
	}
	cout << dinic() << endl;//输出最大流(即最大的匹配数)
	for (int i = 0; i < idx; i += 2) {//枚举正向边,正向边就是idx编号为偶数的边
		if (e[i] > m && e[i] <= n && !f[i]) {//保证这个终点编号在n的区域里
			cout << e[i ^ 1] << " " << e[i] << endl;//并且有流量(连通)
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值