洛谷·[网络流24题]飞行员配对方案问题

谁说我不能喝 提交于 2020-02-05 23:56:18

初见安~这里是传送门:洛谷P2756 飞行员配对方案问题

题解

是个比较明显的网络最大流吧。或者说是二分图匹配求最大匹配数

构造二分图,一边是S连向外籍飞行员,一边是英国飞行员连向T,边权都是1,按照题目给的关系连边,边权INF,跑最大流就好。可以理解成每个外籍飞行员都有一个贡献,但是要选一个英国飞行员然后流向T。【那不就是二分图最大匹配。】所以用二分图也可以写的。这里就用网络流了,好写。

那么还有个问题就是要输出方案。简单啊,看每个外籍or英国飞行员向英国or外籍飞行员连的边的边权,正边看是否满流,逆边看是否有流,满足那么这两个配一架飞机。

上代码——

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 500
#define maxm 200005
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

struct edge {int to, w, nxt;} e[maxm];
int head[maxn], k = 0;
void add(int u, int v, int w) {
	e[k] = {v, w, head[u]}; head[u] = k++;
	e[k] = {u, 0, head[v]}; head[v] = k++;
}

int n, m;
int d[maxn], S, T;
bool bfs() {
	memset(d, 0, sizeof d); d[S] = 1;
	queue<int> q; q.push(S);
	while(q.size()) {
		register int u = q.front(), v; q.pop();
		for(int i = head[u]; ~i; i = e[i].nxt) {
			v = e[i].to; if(e[i].w && !d[v]) {
				d[v] = d[u] + 1; q.push(v);
				if(v == T) return true;
			}
		}
	}
	return false;
}

int Dinic(int u, int flow) {
	if(u == T) return flow;
	register int res = flow, k;
	for(int i = head[u], v; ~i; i = e[i].nxt) {
		v = e[i].to; if(e[i].w && d[v] == d[u] + 1) {
			k = Dinic(v, min(res, e[i].w));
			if(!k) d[v] = 0;
			e[i].w -= k, e[i ^ 1].w += k;
			res -= k;
		}
	}
	return flow - res;
}

signed main() {
	memset(head, -1, sizeof head);
	m = read(), n = read();
	register int u, v, tot = 0;
	while(u = read(), v = read()) {
		if(u == -1) break;
		add(u, v, INF); tot += 2;//记录图中间的边是从0~tot-1
	}
	S = 0, T = n + 1;
	for(int i = 1; i <= m; i++) add(S, i, 1);
	for(int i = m + 1; i <= n; i++) add(i, T, 1);
	
	register int flow, ans = 0;
	while(bfs()) while(flow = Dinic(S, INF)) ans += flow;
	if(!ans) {puts("No Solution!"); return 0;}//一架都配不了
	printf("%d\n", ans);
	for(int i = 0; i < tot; i += 2) //这里判断的是逆边,其实SPJ下顺序都无所谓的
        if(e[i ^ 1].w) printf("%d %d\n", e[i].to, e[i ^ 1].to);
	return 0;
}

迎评:)
——End——

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!