在圖論競賽題中,「最小路徑覆蓋」是將有向無環圖(DAG)轉化為二分圖最大匹配的經典模型。本文以 HDU 1151 為例,完整推導「最小路徑覆蓋 = 頂點數 − 最大匹配數」的數學依據,並給出基於匈牙利演算法的 O(VE) 實作,適合 ACM/ICPC 選手與圖論愛好者深入研讀。
給定一個無環有向圖(DAG),每條邊代表單向街道。若干傘兵可降落在任意路口,並順著邊方向移動。要求每個路口恰好被一位傘兵造訪一次,求最少需要多少傘兵。
等價於:用最少數量的不相交路徑覆蓋所有頂點,即 Minimum Path Cover in DAG。
定理:對任意 DAG
G = (V, E),其最小路徑覆蓋數P_min = |V| − M_max,其中M_max為對應二分圖的最大匹配數。
v ∈ V 拆分為左部 v_in 與右部 v_out。(u → v) ∈ E,在二分圖中加入無向邊 (u_out, v_in)。M 對應原圖中「可合併為同一路徑」的頂點對:若 u_out — v_in ∈ M,表示 u 緊接在 v 之前,兩者屬於同一路徑。|M| 條 ⇒ 可合併 |M| 對頂點 ⇒ 路徑條數減少 |M| 條。|V| 條),合併後剩 |V| − |M| 條。T。n(頂點數 ≤ 120)、m(邊數)。g[u][v](1-based)。ans。n - ans。#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN = 125; // 題目上限 120,稍留餘量
int n, m;
bool g[MAXN][MAXN]; // 鄰接矩陣:g[u][v] = true 表示 u→v
int match[MAXN]; // match[v] = u 表示 v_in 與 u_out 配對
bool vis[MAXN]; // DFS 訪問標記
// 嘗試為左部頂點 u 尋找增廣路徑
bool dfs(int u) {
for (int v = 1; v <= n; ++v) {
if (g[u][v] && !vis[v]) {
vis[v] = true;
if (match[v] == 0 || dfs(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
// 匈牙利演算法主流程
int hungarian() {
memset(match, 0, sizeof(match));
int ans = 0;
for (int u = 1; u <= n; ++u) {
memset(vis, 0, sizeof(vis));
if (dfs(u)) ++ans;
}
return ans;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
memset(g, 0, sizeof(g));
for (int i = 0; i < m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
g[u][v] = true; // 有向邊 u → v
}
int maxMatch = hungarian();
printf("%d\n", n - maxMatch); // 最小路徑覆蓋
}
return 0;
}
O(n * m),n ≤ 120,m ≤ n²,極限約 1.7×10⁶ 次操作,極快。O(n²) 鄰接矩陣,約 15 KB。n=120 遞迴安全,若擴大至 10⁴ 需改迭代或加棧大小。memset 清空全域陣列。O(√V·E),但本題規模匈牙利足夠。掌握「DAG 最小路徑覆蓋 ⇔ 二分圖最大匹配」轉化,不僅能秒殺 HDU 1151,更能舉一反三解決大量調度、覆蓋類競賽題目。