首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

HDU 1151 最小路徑覆蓋:有向無環圖的最大匹配轉化解法詳解

     編輯:關於C++

在圖論競賽題中,「最小路徑覆蓋」是將有向無環圖(DAG)轉化為二分圖最大匹配的經典模型。本文以 HDU 1151 為例,完整推導「最小路徑覆蓋 = 頂點數 − 最大匹配數」的數學依據,並給出基於匈牙利演算法的 O(VE) 實作,適合 ACM/ICPC 選手與圖論愛好者深入研讀。

題目大意

給定一個無環有向圖(DAG),每條邊代表單向街道。若干傘兵可降落在任意路口,並順著邊方向移動。要求每個路口恰好被一位傘兵造訪一次,求最少需要多少傘兵。

等價於:用最少數量的不相交路徑覆蓋所有頂點,即 Minimum Path Cover in DAG。

核心定理與證明大綱

定理:對任意 DAG G = (V, E),其最小路徑覆蓋數 P_min = |V| − M_max,其中 M_max 為對應二分圖的最大匹配數。

構造二分圖

  1. 將每個頂點 v ∈ V 拆分為左部 v_in 與右部 v_out。
  2. 對每條原圖有向邊 (u → v) ∈ E,在二分圖中加入無向邊 (u_out, v_in)。
  3. 此時二分圖的匹配邊集 M 對應原圖中「可合併為同一路徑」的頂點對:若 u_out — v_in ∈ M,表示 u 緊接在 v 之前,兩者屬於同一路徑。

從匹配到路徑覆蓋

  • 匹配邊數 |M| 條 ⇒ 可合併 |M| 對頂點 ⇒ 路徑條數減少 |M| 條。
  • 初始每頂點自成一路徑(共 |V| 條),合併後剩 |V| − |M| 條。
  • 最大匹配使合併數最多,故路徑數最少。

演算法流程

  1. 讀入測資組數 T。
  2. 對每組:讀 n(頂點數 ≤ 120)、m(邊數)。
  3. 建立鄰接矩陣 g[u][v](1-based)。
  4. 跑匈牙利演算法求最大匹配 ans。
  5. 輸出 n - ans。

完整 C++ 實作(含註解)

#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。

常見坑點與優化

  1. 重邊:題目未保證無重邊,矩陣寫法天然去重。
  2. 遞迴深度:n=120 遞迴安全,若擴大至 10⁴ 需改迭代或加棧大小。
  3. 多組測資:每組必須 memset 清空全域陣列。
  4. 更快演算法:Hopcroft-Karp O(√V·E),但本題規模匈牙利足夠。

延伸應用

  • 最小鏈覆蓋:偏序集合(Dilworth 定理)同構於本問題。
  • 任務調度:將任務依賴建模為 DAG,最少機器數 = 最小路徑覆蓋。
  • 網路流視角:源點連左部、右部連匯點、容量皆 1,最大流 = 最大匹配。

掌握「DAG 最小路徑覆蓋 ⇔ 二分圖最大匹配」轉化,不僅能秒殺 HDU 1151,更能舉一反三解決大量調度、覆蓋類競賽題目。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved