【网络流24题】飞行员配对方案问题

题意

N N 个点,其中编号为1M 的点可以和编号为 M+1N M + 1 − N 的点匹配。

已知所有允许的匹配点对 (a,b) ( a , b ) ,求最大匹配数,并输出配对方案。

如果不存在,输出No Solution!

数据范围

1MN100 1 ≤ M ≤ N ≤ 100

题解

裸的二分图最大匹配。

考虑匈牙利算法,复杂度 O(n×m) O ( n × m ) ,复杂度有点高(虽然也能跑过)。

考虑Hopcroft-Karp算法,复杂度 O(n×m) O ( n × m ) ,但是我不会写。

考虑最大流算法。

根据陈胤伯的集训队论文《浅谈图的匹配算法及其应用》,得知利用Dinic算法进行二分图最大匹配的复杂度亦为 O(n×m) O ( n × m ) ,遂选择之。

建图方法很简单,新建源点 S S 和汇点 T S S T 分别与二分图的两部分的每个点连接流量限度为 1 1 的边, 可以匹配的点对 (a,b) 亦连接流量限制为 1 1 的边。跑得 ST 最大流即原二分图最大匹配数。

至于匹配方案,根据残余网络,输出所有剩余流量为 0 0 且不与 ST 相连的边的点对即可。

参考代码

// Copyright 2018, Skqliao
#include <bits/stdc++.h>

#define rg register
#define rep(i, l, r) for (rg int i = (l), _##i##_ = (r); i < _##i##_; ++i)
#define rof(i, l, r) for (rg int i = (l) - 1, _##i##_ = (r); i >= _##i##_; --i)
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) static_cast<int>((x).size())
typedef long long ll;

namespace io {
#ifndef ONLINE_JUDGE
char gc() {
    return getchar();
}
template <class T> inline T gt() {
    register T x;
    std::cin >> x;
    return x;
}
#else
const int MAXSIZE = 1 << 22;
inline char gc() {
    static char In[MAXSIZE], *at = In, *en = In;
    if (at == en) {
        en = (at = In) + fread(In, 1, MAXSIZE, stdin);
    }
    return at == en ? EOF : *at++;
}
template <class T> inline T gt() {
    register char c;
    while (c = gc(), !isdigit(c) && c != '-') {}
    register bool f = c == '-';
    register T x = f ? 0 : c - '0';
    for (c = gc(); isdigit(c); c = gc()) {
        x = x * 10 + c - '0';
    }
    return f ? -x : x;
}
#endif
}  // namespace io

namespace dinic {
const int MAXN = 100 + 5;
const int MAXM = 10000 + 5;
const int INF = INT_MAX;
struct Edge {
    int v, f, nxt;
} E[MAXM << 1];
int N, M, S, T, flow, ffffc, tim;
int H[MAXN], cntE;
int Dis[MAXN], Cur[MAXN], Lv[MAXN];
int Flow[MAXM << 1];
bool bfs() {
    static int Que[MAXN];
    int qh = 0, qt = 0;
    Dis[S] = 1, Lv[S] = ++tim;
    for (Que[qt++] = S; qh < qt;) {
        int &u = Que[qh++];
        if (u == T) {
            return true;
        } else {
            for (int i = H[u]; ~i; i = E[i].nxt) {
                int &v = E[i].v;
                if (E[i].f && Lv[v] != tim) {
                    Dis[v] = Dis[u] + 1;
                    Lv[v] = tim;
                    Que[qt++] = v;
                }
            }
        }
    }
    return false;
}
void addEdge(int u, int v, int f = 1) {
    E[++cntE] = (Edge) {v, f, H[u]};
    H[u] = cntE;
    E[++cntE] = (Edge) {u, 0, H[v]};
    H[v] = cntE;
}
void init() {
    M = io::gt<int>(), N = io::gt<int>();
    S = 0, T = N + 1;
    cntE = -1;
    memset(H, -1, sizeof H);
    while (true) {
        int x = io::gt<int>(), y = io::gt<int>();
        if (x + y == -2) {
            break;
        }
        addEdge(x, y);
    }
    rep(i, 1, M + 1) {
        addEdge(S, i);
    }
    rep(i, M + 1, N + 1) {
        addEdge(i, T);
    }
}
int dfs(int x, int maxf) {
    if (x == T || !maxf) {
        return maxf;
    }
    int left = maxf;
    for (int &i = Cur[x]; ~i; i = E[i].nxt) {
        int &v = E[i].v;
        if (Dis[v] == Dis[x] + 1 && E[i].f && Lv[v] == tim) {
            int flow = dfs(v, std::min(E[i].f, left));
            if (!flow) {
                Dis[v] = -1;
            } else {
                left -= flow;
                E[i].f -= flow;
                Flow[i ^ 1] += flow;
                ffffc = 1;
                if (!left) {
                    return maxf;
                }
            }
        }
    }
    return maxf - left;
}
int dinic() {
    int ans = 0;
    while (true) {
        while (bfs()) {
            memcpy(Cur, H, sizeof(int) * (N + 1));
            int res = dfs(S, INF);
            ans += res;
        }
        if (!ffffc) {
            break;
        }
        rep(i, 0, cntE + 1) {
            E[i].f += Flow[i];
        }
        memset(Flow, 0, sizeof(int) * (cntE + 1));
        ffffc = 0;
    }
    return ans;
}
void match() {
    init();
    int res = dinic();
    if (res == 0) {
        puts("No Solution!");
    } else {
        printf("%d\n", res);
        rep(i, 1, M + 1) {
            for (int j = H[i]; ~j; j = E[j].nxt) {
                if(!E[j].f && E[j].v != S && E[j].v != T) {
                    printf("%d %d\n", i, E[j].v);
                    break;
                }
            }
        }
    }
}
}  // namespace dinic

int main() {
    dinic::match();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络算法可以用来解决最大最小割问题,而飞行员配对问题可以转化成最大问题。下面是一个基于 Python 的网络算法求解飞行员配对方案问题的示例代码: ```python from collections import defaultdict class Graph: def __init__(self): self.graph = defaultdict(dict) def add_edge(self, u, v, w): self.graph[u][v] = w def bfs(self, s, t, parent): visited = set() queue = [s] visited.add(s) while queue: u = queue.pop(0) for v in self.graph[u]: if v not in visited and self.graph[u][v] > 0: queue.append(v) visited.add(v) parent[v] = u return True if t in visited else False def max_flow(self, source, sink): parent = {} max_flow = 0 while self.bfs(source, sink, parent): path_flow = float('inf') s = sink while s != source: path_flow = min(path_flow, self.graph[parent[s]][s]) s = parent[s] max_flow += path_flow v = sink while v != source: u = parent[v] self.graph[u][v] -= path_flow self.graph[v][u] += path_flow v = parent[v] return max_flow def pilot_pairing(pilots, planes): num_pilots = len(pilots) num_planes = len(planes) graph = Graph() source = 's' sink = 't' for i in range(num_pilots): graph.add_edge(source, 'P{}'.format(i), 1) for j in range(num_planes): if pilots[i][0] <= planes[j][0] and pilots[i][1] >= planes[j][1]: graph.add_edge('P{}'.format(i), 'A{}'.format(j), 1) graph.add_edge('A{}'.format(i), sink, 1) return graph.max_flow(source, sink) # 示例用法 pilots = [(1, 5), (2, 4), (3, 6), (2, 5)] planes = [(1, 4), (2, 5), (3, 6)] print(pilot_pairing(pilots, planes)) # 输出为 3 ``` 在上面的示例代码中,我们定义了一个 `Graph` 类来表示图,其中 `add_edge` 方法用于添加边,`bfs` 方法用于执行广度优先搜索,`max_flow` 方法用于计算最大。 在 `pilot_pairing` 函数中,我们首先创建了一个 `Graph` 对象,然后为源点和汇点分别添加一条边,并遍历所有的飞行员和飞机,如果某个飞行员可以驾驶某个飞机,则在他们之间添加一条边。最后调用 `max_flow` 方法计算最大。 在 `max_flow` 方法中,我们首先执行广度优先搜索来寻找一条增广路径,然后计算路径上的最小剩余容量,更新路径上的边的量,并更新最大的值。重复执行这个过程,直到没有增广路径为止。最后返回最大的值。 在上面的示例中,我们输入了 4 个飞行员和 3 个飞机,输出得到了最大为 3,说明有 3 对飞行员和飞机可以配对成功。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值