网络流 - 二分图匹配 - 飞行员配对方案问题 - 网络流24题 - 洛谷 P2756
题目描述
一共有 n 个飞行员,其中有 m 个外籍飞行员和 (n - m) 个英国飞行员,外籍飞行员从 1 到 m 编号,英国飞行员从 m + 1 到 n 编号。 对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。
输入格式
输入的第一行是用空格隔开的两个正整数,分别代表外籍飞行员的个数 m 和飞行员总数 n。
从第二行起到倒数第二行,每行有两个整数 u, v 代表外籍飞行员 u 可以和英国飞行员 v 配合。
输入的最后一行保证为 -1 ,代表输入结束。
输出格式
本题存在 Special Judge。
请输出能派出最多的飞机数量,并给出一种可行的方案。
输出的第一行是一个整数,代表一次能派出的最多飞机数量,设这个整数是 k。
第 2 行到第 k + 1 行,每行输出两个整数 u, v 代表在你给出的方案中,外籍飞行员 u 和英国飞行员 v 配合。这 k 行的 u 与 v 应该互不相同。
输入输出样例
输入 #1
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1
输出 #1
4
1 7
2 9
3 8
5 10
说明/提示
【数据范围与约定】
对 于 100 % 的 数 据 , 保 证 1 ≤ m ≤ n < 100 , 1 ≤ u ≤ m < v ≤ n , 同 一 组 配 对 关 系 只 会 给 出 一 次 。 对于 100\% 的数据,保证 1 \leq m \leq n < 100,1 \leq u \leq m < v \leq n,同一组配对关系只会给出一次。 对于100%的数据,保证1≤m≤n<100,1≤u≤m<v≤n,同一组配对关系只会给出一次。
分析:
二 分 图 的 最 大 匹 配 问 题 。 二分图的最大匹配问题。 二分图的最大匹配问题。
相 连 接 的 两 点 之 间 连 一 条 权 值 为 1 的 正 向 边 , 一 条 权 值 为 0 的 反 向 边 , 保 证 了 每 个 点 只 与 一 个 点 匹 配 。 相连接的两点之间连一条权值为1的正向边,一条权值为0的反向边,保证了每个点只与一个点匹配。 相连接的两点之间连一条权值为1的正向边,一条权值为0的反向边,保证了每个点只与一个点匹配。
源 点 S 向 一 侧 的 所 有 点 ( 外 籍 飞 行 员 ) 连 接 一 条 权 值 为 1 的 正 向 边 , 权 值 为 0 的 反 向 边 源点S向一侧的所有点(外籍飞行员)连接一条权值为1的正向边,权值为0的反向边 源点S向一侧的所有点(外籍飞行员)连接一条权值为1的正向边,权值为0的反向边
另 一 侧 的 所 有 点 ( 英 国 飞 行 员 ) 向 汇 点 T 连 接 一 条 权 值 为 1 的 正 向 边 , 权 值 为 0 的 反 向 边 另一侧的所有点(英国飞行员)向汇点T连接一条权值为1的正向边,权值为0的反向边 另一侧的所有点(英国飞行员)向汇点T连接一条权值为1的正向边,权值为0的反向边
那 么 一 条 残 留 网 络 就 建 好 了 。 那么一条残留网络就建好了。 那么一条残留网络就建好了。
在 这 个 网 络 中 跑 最 大 流 , 该 最 大 流 就 对 应 一 个 最 大 匹 配 。 在这个网络中跑最大流,该最大流就对应一个最大匹配。 在这个网络中跑最大流,该最大流就对应一个最大匹配。
然 后 我 们 遍 历 残 留 网 络 中 的 所 有 边 , 若 这 条 边 满 流 , 且 这 条 边 的 端 点 编 号 是 飞 行 员 的 编 号 , 然后我们遍历残留网络中的所有边,若这条边满流,且这条边的端点编号是飞行员的编号, 然后我们遍历残留网络中的所有边,若这条边满流,且这条边的端点编号是飞行员的编号,
那 么 这 条 边 就 是 一 组 匹 配 , 输 出 这 条 边 的 两 个 端 点 即 可 。 那么这条边就是一组匹配,输出这条边的两个端点即可。 那么这条边就是一组匹配,输出这条边的两个端点即可。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 210, M = N * N, inf = 0x3f3f3f3f;
int n, m, S, T;
int e[M], ne[M], f[M], h[N], idx;
int cur[N], q[N], d[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], f[idx] = c, h[a] = idx ++;
}
void build()
{
memset(h, -1, sizeof h);
for(int i = 1; i <= m; i ++) add(S, i, 1), add(i, S, 0);
for(int i = m + 1; i <= n; i ++) add(i, T, 1), add(T, i, 0);
int a, b;
while(~scanf("%d%d", &a, &b), a != -1 || b != -1)
{
add(a, b, 1), add(b, a, 0);
}
}
bool bfs()
{
int hh = 0, tt = -1;
memset(d, -1, sizeof d);
d[S] = 0, q[++ tt] = S, cur[S] = h[S];
while(hh <= tt)
{
int u = q[hh ++];
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(d[v] == -1 && f[i] > 0)
{
d[v] = d[u] + 1;
cur[v] = h[v];
if(v == T) return true;
q[++ tt] = v;
}
}
}
return false;
}
int find(int u, int limit)
{
if(u == T) return limit;
int flow = 0, t;
for(int i = cur[u]; ~i && flow < limit; i = ne[i])
{
int v = e[i];
cur[u] = i;
if(d[v] == d[u] + 1 && f[i] > 0)
{
t = find(v, min(f[i], limit - flow));
if(!t) d[v] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, inf)) res += flow;
return res;
}
int main()
{
scanf("%d%d", &m, &n);
S = 0, T = n + 1;
build();
printf("%d\n", dinic());
for(int i = 0; i < idx; i += 2)
{
int j = e[i];
if(f[i] == 0 && j >= m + 1 && j <= n)
printf("%d %d\n", e[i + 1], j);
return 0;
}