双调欧几里得旅行商问题(双调TSP)CLRS算法导论 思考题15-3 讲解(动态规划证明最优子结构)以及代码

双调欧几里得旅行商问题

题目

欧几里得旅行商问题中,给定平面$n$个点作为输入,希望求出连接所有$n$个点的最短巡游路线。此问题是NP难问题,因此不存在多项式时间的求解算法。

J.L.Bentley建议将问题简化,限制巡游路线为双调巡游(bitonic tours):从最左边的点开始,严格向右前进,直至最右边的点,然后调头严格向左前进,直至回到起始点。问题简化后存在多项式时间算法。

要求:设计一个$\mathcal{O}(n^2)$时间的最优双调巡游路线算法。假设任何两个点的$x$坐标均不同,且所有实数运算都花费单位时间。


最优解的结构特征

标准TSP的状态压缩解法无法达到$\mathcal{O}(n^2)$复杂度,而双调巡游具有x坐标单调的约束,我们可以利用该特性拆分子问题:

  1. 预处理:将所有点按$x$坐标从小到大排序,记为:
    $$p_1, p_2, \ldots, p_n \ (x_1 < x_2 < \ldots < x_n)$$
  2. 核心思路:合法双调巡游可拆分为两条从$p_1$出发的单调向右路径,最终在$p_n$处汇合形成回路,两条路径覆盖所有点且不重复。

子问题定义

定义$B(i,j)$:覆盖点$p_1,p_2,\dots,p_i$的两条不相交路径,满足:

  1. 所有点仅出现一次;
  2. 两条路径均从$p_1$开始;
  3. 路径末端为$p_i$和$p_j$($1 \leq j < i$);
  4. 每条路径严格向右(x坐标递增)。

最优子结构

  1. 若 $i > j + 1$
    最优解$B(i,j)$中必有边$(p_{i-1}, p_i)$,去掉该边后,剩余结构为子问题$B(i-1,j)$的最优解。

  2. 若 $i = j + 1$
    存在$k < j$,最优解$B(i,j)$中必有边$(p_k, p_i)$,去掉该边后,剩余结构为子问题$B(i-1,k)$的最优解。


最优子结构证明

情况1:$i > j+1$

  • $B(i,j)$的末端为$p_i$、$p_j$,$j < i-1$,因此$p_{i-1}$不是路径末端;
  • 路径必须严格向右,$p_{i-1}$只能连接到唯一更右侧的点$p_i$;
  • 若去掉边$(p_{i-1},p_i)$后剩余结构不是$B(i-1,j)$的最优解,则可构造更优的$B(i,j)$,与最优性矛盾。

情况2:$i = j+1$

  • 两条路径末端为$p_i$和$p_{i-1}$,若连接$p_{i-1}$和$p_i$会破坏双路径结构;
  • 因此$p_i$必须连接到另一条路径的末端$p_k$($k<j$);
  • 同理可证,去掉边$(p_k,p_i)$后剩余结构为$B(i-1,k)$的最优解。

递归求解方案

定义$OPT(i,j)$为$B(i,j)$的最短路径长度,$d(a,b)$表示点$a$和点$b$的欧几里得距离。

状态转移方程

  1. 初始条件:$OPT(2,1) = d(p_1,p_2)$
  2. 当 $i > j + 1$
    $$OPT(i,j) = OPT(i-1,j) + d(p_{i-1}, p_i)$$
  3. 当 $i = j + 1$
    $$OPT(i,j)=\min_{k < j} {OPT(i-1,k) + d(p_k, p_i)}$$

动态规划算法实现

算法:BITONIC-TSP

BITONIC-TSP(P)
    按x坐标对P排序
    n = 点的数量
    初始化 OPT[1..n][1..n] 二维表
    初始化 PREV[1..n][1..n] 前驱记录表
    OPT[2][1] = 点p1到p2的距离
    PREV[2][1] = (1, 1)
    
    for i = 3 to n:
        // 情况1:i > j+1
        for j = 1 to i-2:
            OPT[i][j] = OPT[i-1][j] + dist(P[i-1], P[i])
            PREV[i][j] = (i-1, j)
        
        // 情况2:i = j+1
        OPT[i][i-1] = 无穷大
        for k = 1 to i-2:
            val = OPT[i-1][k] + dist(P[k], P[i])
            if val < OPT[i][i-1]:
                OPT[i][i-1] = val
                PREV[i][i-1] = (i-1, k)
    return OPT, PREV

变量说明

  • $OPT[i][j]$:覆盖前$i$个点,路径端点为$p_i$和$p_j$($i>j$)的最短双调路径长度;
  • $PREV[i][j]$:记录状态$(i,j)$的前驱状态,用于还原路径。

复杂度分析

  • 排序:$\mathcal{O}(n\log n)$
  • 状态总数:$\frac{n(n-1)}{2}=\mathcal{O}(n^2)$
  • 总时间复杂度:$\boldsymbol{\mathcal{O}(n^2)}$
  • 空间复杂度:$\boldsymbol{\mathcal{O}(n^2)}$

最终闭合回路

最短双调巡游长度 = $OPT[n][n-1] + d(p_{n-1}, p_n)$


最优路径构造

算法:RECONSTRUCT

RECONSTRUCT(PREV, P, n)
    i = n
    j = n - 1
    left = []
    right = []
    while i > 1:
        prev_i, prev_j = PREV[i][j]
        if prev_j == j:
            left.append(P[i])
            i = i - 1
        else:
            right.append(P[i])
            i = i - 1
            j = prev_j
    left.append(P[1])
    反转left
    tour = left + right
    return tour

复杂度

  • 时间复杂度:$\mathcal{O}(n)$(仅需回溯$n$次)

总结

  1. 核心思想:利用双调巡游的单调性,将问题拆分为两条向右的路径,用动态规划求解;
  2. 状态设计:$OPT[i][j]$记录两条路径的端点,保证无后效性;
  3. 时间复杂度:$\mathcal{O}(n^2)$,完美满足题目要求;
  4. 完整流程:排序→动态规划填表→回溯构造最优路径。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const double INF = 1e18;
class Point {
    public:
    double x, y;
};
// 欧几里得距离
double dist(Point a, Point b) {
    return hypot(a.x - b.x, a.y - b.y);
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int n;
    while (cin >> n) {
        vector<Point> P(n + 1); // 1-based
        for (int i = 1; i <= n; i++) {
            cin >> P[i].x >> P[i].y;
        }
        vector<vector<double>> OPT(n + 1, vector<double>(n + 1, INF));
        OPT[2][1] = dist(P[1], P[2]);
        for (int i = 3; i <= n; i++) {
            for (int j = 1; j <= i - 2; j++) {
                OPT[i][j] = OPT[i - 1][j] + dist(P[i - 1], P[i]);
            }
            OPT[i][i - 1] = INF;
            for (int k = 1; k <= i - 2; k++) {
                double val = OPT[i - 1][k] + dist(P[k], P[i]);
                OPT[i][i - 1] = min(OPT[i][i - 1], val);
            }
        }
        double ans = OPT[n][n - 1] + dist(P[n - 1], P[n]);
        cout << fixed << setprecision(2) << ans << "\n";
    }
    return 0;
}
posted @ 2026-05-13 21:26  Aloutte  阅读(7)  评论(0)    收藏  举报