网络流 - 二分图匹配 - 飞行员配对方案问题 - 网络流24题 - 洛谷 P2756

网络流 - 二分图匹配 - 飞行员配对方案问题 - 网络流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%1mn<1001um<vn


分析:

二 分 图 的 最 大 匹 配 问 题 。 二分图的最大匹配问题。

相 连 接 的 两 点 之 间 连 一 条 权 值 为 1 的 正 向 边 , 一 条 权 值 为 0 的 反 向 边 , 保 证 了 每 个 点 只 与 一 个 点 匹 配 。 相连接的两点之间连一条权值为1的正向边,一条权值为0的反向边,保证了每个点只与一个点匹配。 10

源 点 S 向 一 侧 的 所 有 点 ( 外 籍 飞 行 员 ) 连 接 一 条 权 值 为 1 的 正 向 边 , 权 值 为 0 的 反 向 边 源点S向一侧的所有点(外籍飞行员)连接一条权值为1的正向边,权值为0的反向边 S10

另 一 侧 的 所 有 点 ( 英 国 飞 行 员 ) 向 汇 点 T 连 接 一 条 权 值 为 1 的 正 向 边 , 权 值 为 0 的 反 向 边 另一侧的所有点(英国飞行员)向汇点T连接一条权值为1的正向边,权值为0的反向边 T10

那 么 一 条 残 留 网 络 就 建 好 了 。 那么一条残留网络就建好了。

在 这 个 网 络 中 跑 最 大 流 , 该 最 大 流 就 对 应 一 个 最 大 匹 配 。 在这个网络中跑最大流,该最大流就对应一个最大匹配。

然 后 我 们 遍 历 残 留 网 络 中 的 所 有 边 , 若 这 条 边 满 流 , 且 这 条 边 的 端 点 编 号 是 飞 行 员 的 编 号 , 然后我们遍历残留网络中的所有边,若这条边满流,且这条边的端点编号是飞行员的编号,

那 么 这 条 边 就 是 一 组 匹 配 , 输 出 这 条 边 的 两 个 端 点 即 可 。 那么这条边就是一组匹配,输出这条边的两个端点即可。

代码:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值