飞行员配对方案问题

题目大意

Special Judge
\(m\) 个外籍飞行员和 \((n-m)\) 个英国飞行员,一架飞机由一个外籍飞行员和一个英国飞行员组成,给定一些外籍和英国飞行员的组合,问怎样才能派出最多的飞机,并给出方案。
这是一道经典的最大流,最小割问题,第一问很简单,难在第二问输出方案

思路

算法一 (匈牙利二分图匹配)

将英国飞行员看作一个点集,外籍飞行员看作另一个点集,跑一边二分图匹配就可以了。

实际上,二分图匹配也是一种特殊的最大流EK算法

时间复杂度 \(O(nm)\)

算法二(最大流dinic)

dinic算法是用于求最大流的一种算法,相比于EK算法,他的速度会快一些。

没学过dinic算法的可以看我这篇博客。

网络流的题目一般都不是难在代码上,而是难在建图,这是这道题的建图:

建立一个源点,连向所有的外籍飞行员,权值为 \(1\)
建立一个汇点,所有的英国飞行员连向它,权值为 \(1\)
根据题目给的关系,将其连边,权值为INF

由于这个图中所有的权值和是确定的,所以就将题目要求转换为了最小割问题,又因为最大流最小割是等价的,所以跑一遍dinic就可以了。

然后是第二问,首先需要理解,因为跑完dinic后留下的图中一定是无法从源点再次走到汇点的。所以跑完的图中就相当于是最小割的结果,于是只要在图中找到一对关系,使得反边的权值不等于零即可。

时间复杂度 \(O(nm^2)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
int n, m, S = 0, T, ans, dep[N];
struct Edge {
    int v, id, w, last, tag;
};
vector<Edge> g[N];

void add(int x, int y, int w) {
    g[x].push_back({y, g[y].size(), w, w, 1});
    g[y].push_back({x, g[x].size() - 1, 0, 0, 2}); // 注意这里反边的权值要设为0 !!!不然输出方案时可能会错
}

bool bfs() {
	memset(dep, 0x3f, sizeof dep);
    queue<int> q;
    q.push(S);
    dep[S] = 1;
    while (q.size()) {
        int u = q.front(); q.pop();
        for (auto t : g[u]) {
            int v = t.v, w = t.w;
            if (dep[v] != 0x3f3f3f3f || !w) continue;
            dep[v] = dep[u] + 1;
            q.push(v);
        }
    }
    return dep[T] != 0x3f3f3f3f;
}

int dfs(int x, int flow) {
    if (x == T) return flow;
    int sum = 0;
    for (auto &t : g[x]) {
        int y = t.v, w = t.w, id = t.id;
        if (!w || dep[y] != dep[x] + 1) continue;
        int f = dfs(y, min(flow, w));
        if (!f) continue;
        sum += f;
        flow -= f;
        t.w -= f;
        g[y][id].w += f;
        if (!flow) break;
    }
    return sum;
}

void dinic() {
    while (bfs()) {
        ans += dfs(S, 0x3f3f3f3f);
    }
}

int main() {
    cin >> m >> n; 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); // 这里一定要写add(i, T, 1),而不能写成add(T, i, 1),要不然你可能会和我一样得到63分的高分
    
    int u, v;
    while (cin >> u >> v) {
        if (u == -1) break;
        add(u, v, 0x3f3f3f3f);
    }

    dinic();
    cout << ans << endl;
    for (int i = 1; i <= n; i++)
        for (auto t : g[i]) {
            if (t.tag == 1) continue;
            if (t.w != 0 && t.v != T && t.v != S) {
                int x = min(i, t.v), y = max(i, t.v);
                cout << x << " " << y << endl;
            }
        }
    
    return 0;
}
posted @ 2025-07-05 23:15  wuzihenb  阅读(17)  评论(0)    收藏  举报