羅区P2756--パイロットプログラムマッチング問題[部グラフの最大マッチングアルゴリズム&最大流量Dinic]

トピックポータル


トピックの背景

第二次世界大戦...


タイトル説明

RAFのパイロットは外国の多数の秋から募集しました。空軍によって送信され、各航空機はセーリングのスキルや言語に2人のパイロットが装備される英国のパイロットであり、もう一つは外国のパイロットである、相互に協力することができます。パイロットの中で、すべての外国人のパイロットは、他のイギリスのパイロットの数でうまく動作することができます。飛行パイロットを作るためにペアを選択する方法航空機まで送られます。外国人パイロットと英国のパイロットとの与えられた状況のために、テストパイロットは最高のペアリング方法、王立空軍を見つけるためにアルゴリズムを設計するためには、飛行機を送ることができます。

最高のペアリング方法を見つけるために外国人パイロットと英国のパイロット、パイロットプログラムで与えられた状況のために、空軍は、一つは飛行機を送ることができます。


入力形式

1行目は、2つの正の整数で、mとnました。N RAF(N <100)パイロットの総数であり、Mは、外部パイロットの数である(M <= N)。英国のパイロット数m + 1〜N;外国パイロットが1〜M番。

次に、各ラインは、2つの正の整数i、jは、iのjに外国人パイロットと英国のパイロットを表すことができますがあります。最後に、2 -1の終わりに。


出力フォーマット

1行目は最高のペアリング方法は、一度M.を送ったことができ、航空機のパイロットの最大数です 次のMラインは最高のパイロットプログラムを対になっています。各行は、i、jは、ペアリング方式で最高のパイロットを表す2つの正の整数、パイロットパイロットiとjのペアを持っています。あなたは、その後、出力「いいえソリューションを!」が存在しないスキームをペアリング最高のパイロットをお願いします。


エントリー

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


問題の解決策

  • 明らかに、2部グラフの最大マッチングが、我々は最大フロー問題に変換することができます
  • ソースとシンクを追加し、その後、フル右側が1に設定されている、側Dinicを実行することができます
    ここに画像を挿入説明

AC-コード

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int inf = 0x3f3f3f3f;
const int maxn = 50 * 50 + 50 * 2;
int n, m;

struct Edge {
	int to;
	int next;
	int val;
}edge[maxn << 1]; // 双向边,开 2 倍数组
int head[maxn];
int cnt; // 边的数量,从 0 开始编号
int depth[maxn]; // 分层图标记深度
int cur[maxn]; // 当前弧优化,记录当前点 u 循环到了哪一条边
void add(int u, int v, int w) {
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	edge[cnt].val = w;
	head[u] = cnt++;
}

// bfs分层图
bool bfs(int s, int t) {
	queue<int>q;
	memset(depth, 0, sizeof depth);
	depth[s] = 1; // 源点深度为 1
	cur[s] = head[s];
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int i = head[u]; ~i; i = edge[i].next) {
			int v = edge[i].to;
			if (edge[i].val > 0 && depth[v] == 0) { // 如果残量不为0,且 v 还未分配深度
				depth[v] = depth[u] + 1;
				cur[v] = head[v]; //------当前弧优化,注意在bfs里,这样做到了“按需赋值”,因为Dinic本来上界就松得一匹, BFS的过程中不连通的点根本就不用再管了...
				if (v == t) // -----分层图汇点优化:遇到汇点直接返回------
					return true;
				q.push(v);
			}
		}
	}
	return depth[t]; // 汇点深度为 0:不存在分层图,返回false;
					 //           非 0 :存在增广路,返回true
}

// dfs寻找增广路
int dfs(int u, int flow, int t) {
	if (u == t || flow <= 0) // 到达汇点
		return flow;
	int rest = flow;
	for (int i = cur[u]; ~i; i = edge[i].next) {
		int v = edge[i].to;
		if (depth[v] == depth[u] + 1 && edge[i].val != 0) { // 满足分层图、残量>0 两个条件
			int k = dfs(v, min(rest, edge[i].val), t); // 向下增广
			if (k < 0) // ------无用点优化-----
				depth[v] = 0;
			rest -= k;
			edge[i].val -= k; // 正向边减
			edge[i ^ 1].val += k; // 反向边加
			if (rest <= 0) //------剩余量优化:在进行增广的时候,如果该节点已经没有流量,直接退出------
				break;
		}
	}
	return flow - rest; // flow:推送量,rest:淤积量,flow - rest:接受量/成功传递量
}
int Dinic(int s, int t) {
	int ans = 0;
	while (bfs(s, t)) {
		ans += dfs(s, inf, t);
	}
	return ans;
}
int main() {
	ios;
	while (cin >> m >> n) {
		::cnt = 0;
		memset(head, -1, sizeof head);
		for (int i = 1; i <= m; i++) {
			add(0, i, 1);
			add(i, 0, 0);
		}
		for (int i = m + 1; i <= n; i++) {
			add(i, n + 1, 1);
			add(n + 1, i, 0);
		}
		for (int a, b; cin >> a >> b;) {
			if (a == -1 && b == -1)		break;
			add(a, b, 1);
			add(b, a, 0);
		}
		int ans = Dinic(0, n + 1);
		if (ans) {
			cout << ans << endl;
			for (int i = 1; i <= m; i++) {
				for (int u = head[i]; ~u; u = edge[u].next) {
					if (edge[u].to == 0)	continue;
					if (edge[u].val == 0) {
						cout << i << " " << edge[u].to << endl;
						break;
					}
				}
			}
		}
		else {
			cout << "No Solution!" << endl;
		}

	}
}


公開された104元の記事 ウォン称賛60 ビュー5845

おすすめ

転載: blog.csdn.net/Q_1849805767/article/details/103648310