跳转至

汉密尔顿回路与路径 (Hamiltonian Cycle & Path)

  • 汉密尔顿路径 (Hamiltonian Path):经过图中每个顶点恰好一次的路径。
  • 汉密尔顿回路 (Hamiltonian Cycle):起点与终点相同,且除起点外经过每个顶点恰好一次的回路。

不同于可以在 \(\mathcal{O}(V + E)\) 判定的欧拉回路,判定一般图中是否存在汉密尔顿回路是一个著名的 NP-完全问题 (NP-Complete)


经典充分性判定定理

对于包含 \(n \ge 3\) 个顶点的无向简单图:

  1. 狄拉克定理 (Dirac's Theorem):若对图中任意顶点 \(u\),均有其度数 \(\deg(u) \ge \lceil n / 2 \rceil\),则图一定存在汉密尔顿回路。
  2. 奥尔定理 (Ore's Theorem):若对图中任意一对不相邻的顶点 \(u, v\),均满足 \(\deg(u) + \deg(v) \ge n\),则图一定存在汉密尔顿回路。

状态压缩动态规划求解 (TSP / 最短汉密尔顿回路)

在算法竞赛中,当 \(n \le 20 \sim 22\) 时,通常采用状态压缩 DP (Bitmask DP) 求解最短汉密尔顿路径或回路(旅行商问题,TSP)。

  • 设二进制掩码 \(mask \in [1, 2^n - 1]\) 表示已访问顶点的集合(第 \(i\) 位为 \(1\) 表示点 \(i\) 已访问)。
  • 定义 \(dp[mask][u]\) 表示当前已走过的节点集合为 \(mask\),且当前停留在节点 \(u\) 时的最小路径权值。
  • 状态转移方程:

    \[ dp[mask \mid (1 \ll v)][v] = \min \big( dp[mask \mid (1 \ll v)][v],\; dp[mask][u] + \text{weight}(u, v) \big) \]

时间复杂度:\(\mathcal{O}(2^n \cdot n^2)\),空间复杂度:\(\mathcal{O}(2^n \cdot n)\)

#include <vector>
#include <algorithm>

constexpr long long INF = 0x3f3f3f3f3f3f3f3fLL;

// dist[u][v] 为 u 到 v 的有向边权 (不存在边设为 INF)
// 返回从起点 0 出发,遍历所有节点恰好一次的最短路径长度
long long hamiltonian_path(int n, const std::vector<std::vector<long long>> &dist) {
    int total_states = 1 << n;
    std::vector<std::vector<long long>> dp(total_states, std::vector<long long>(n, INF));

    dp[1][0] = 0; // 起点为 0,初始状态仅包含点 0 (掩码二进制 00...001)

    for (int mask = 1; mask < total_states; ++mask) {
        for (int u = 0; u < n; ++u) {
            if (dp[mask][u] == INF) continue;

            // 枚举下一个前往的顶点 v
            for (int v = 0; v < n; ++v) {
                if (!(mask & (1 << v)) && dist[u][v] != INF) {
                    int next_mask = mask | (1 << v);
                    dp[next_mask][v] = std::min(dp[next_mask][v], dp[mask][u] + dist[u][v]);
                }
            }
        }
    }

    // 求访问完所有点 (mask == (1 << n) - 1) 的最短路径
    long long ans = INF;
    for (int u = 0; u < n; ++u) {
        ans = std::min(ans, dp[total_states - 1][u]);
    }
    return ans;
}

// 求解汉密尔顿回路 (最后回到起点 0)
long long tsp_cycle(int n, const std::vector<std::vector<long long>> &dist) {
    int total_states = 1 << n;
    std::vector<std::vector<long long>> dp(total_states, std::vector<long long>(n, INF));
    dp[1][0] = 0;

    for (int mask = 1; mask < total_states; ++mask) {
        for (int u = 0; u < n; ++u) {
            if (dp[mask][u] == INF) continue;
            for (int v = 0; v < n; ++v) {
                if (!(mask & (1 << v)) && dist[u][v] != INF) {
                    dp[mask | (1 << v)][v] = std::min(dp[mask | (1 << v)][v], dp[mask][u] + dist[u][v]);
                }
            }
        }
    }

    long long min_cycle = INF;
    for (int u = 1; u < n; ++u) {
        if (dp[total_states - 1][u] != INF && dist[u][0] != INF) {
            min_cycle = std::min(min_cycle, dp[total_states - 1][u] + dist[u][0]);
        }
    }
    return min_cycle;
}