分支限界法解决旅行售货员问题

447 阅读2分钟

今天我们通过旅行商问题学习分支限界法的思想与一般流程,并将其与回溯法进行一定的对比。

题目描述

image.png

在如上图例中,共有四个节点代表四个城市,节点与节点之间有一条赋权的边,代表城市与城市之间的道路长度。

现在要求从一个城市开始,走遍所有城市并回到起点城市所走过的最小路径。

题目分析

首先我们知道,最终形成的路径是一个闭环,这也便意味着无论选择哪个起点,最终的最短路径是一样的。在这里,我们选择节点 11 作为起点进行求解。步骤如下:

1)对定义每个节点的结构体,其包括当前节点的编号 idxidx,已走路程(实际位移长度)uu,向下走完全程的下界位移长度(一般取不到)dd。已走过的路线数组 vv,已走过的节点数组 stst,并重载了大于号(后续优先队列的排序)。

2)计算每个节点的最小出边。

3)进行最优选择的广度搜素。首先建立根节点并将其置入优先队列。若队列不空,每次提取出 u+du + d 数值最小的节点,这个节点意味着在目前情况下能够达到的下界值中数值最小的节点,即当前来看期望最优的路线。然后以此为父节点,将其子节点更新数值并加入优先队列,依次递推。当走过全程时,更新答案,并每次操作新节点时将新节点的下界值与答案对比,若大于则跳出,若当前节点已走完全程且新答案小于目前答案,则更新答案。注意对路线数组 vv 的更新。

Accept代码

#include <bits/stdc++.h>

using namespace std;
const int N = 10;

int g[N][N], mi[N];
int ans = 100;
vector<int> f;
int n = 4;

struct P
{
    int idx;
    int u, d;
    vector<int> v;
    bool st[N];

    bool operator> (const P& f)const
    {
        return u + d > f.u + f.d;
    }
} ;

int main()
{
    g[1][2] = 30, g[1][3] = 6, g[1][4] = 4;
    g[2][1] = 30, g[2][3] = 5, g[2][4] = 10;
    g[3][1] = 6, g[3][2] = 5, g[3][4] = 20;
    g[4][1] = 4, g[4][2] = 10, g[4][3] = 20;
    priority_queue<P, vector<P>, greater<P>> pq;

    int s = 0;
    for (int i = 1; i <= n; i ++)
    {
        mi[i] = 100;
        for (int j = 1; j <= n; j ++)
            if (g[i][j]) mi[i] = min(mi[i], g[i][j]);
        s += mi[i];
    }

    P root; root.idx = 1;
    root.u = 0, root.d = s; 
    root.v.push_back(1); 
    memset(root.st, false, sizeof root.st); root.st[1] = true;

    pq.push(root);
    while (!pq.empty())
    {
        P t = pq.top(); pq.pop();
        if (t.u + t.d >= ans) break;
        if (t.v.size() == n)
        {
            P x = t; x.idx = 1;
            x.u += g[t.idx][1], x.d -= mi[t.idx];
            x.v.push_back(1);
            pq.push(x);
            continue;
        }
        else if (t.v.size() == n + 1)
        {
            if (ans > t.u) ans = t.u, f = t.v;
            else break;
            continue;
        }
        for (int i = 1; i <= n; i ++)
        {
            if (!t.st[i])
            {
                P x = t; x.idx = i;
                x.u += g[t.idx][i], x.d -= mi[t.idx];
                x.v.push_back(i); x.st[i] = true;
                pq.push(x);
            }
        }
    }
    cout << ans << "\n";
    for (auto i : f) cout << i << ' ';
    return 0;
}