c++ 如何使用DP和位掩码解决旅行商问题中的路径跟踪

zf2sa74q  于 2023-11-19  发布在  其他
关注(0)|答案(1)|浏览(116)

我写了代码来解决TSP(旅行推销员问题)使用DP,但我不知道如何跟踪路径,给出解决方案。
这是代码:

static int tsp(std::vector<std::vector<int>> const& dist, size_t current, uint64_t mask, std::vector<std::vector<int>>& dp) {
    if (mask == ((size_t)1 << dist.size()) - 1)
        return dist[current][0];

    if (dp[mask][current] != -1)
        return dp[mask][current];

    int result = INT_MAX;
    for (uint64_t i = 0; i < dist.size(); ++i) {
        uint64_t val = (uint64_t)1 << i;
        if (mask & val)
            continue;

        int sub = dist[current][i] + tsp(dist, i, mask | val, dp);
        result = std::min(result, sub);
    }

    dp[mask][current] = result;
    return result;
}

int main()
{
    std::vector<std::vector<int>> dist{
        {0, 10, 41, 31},
        {10, 0, 30, 19},
        {41, 30, 0, 20},
        {31, 19, 20, 0}
    };

    std::vector<std::vector<int>> dp(1 << dist.size(), std::vector<int>(dist.size(), -1));
    std::cout << (tsp(dist, 0, 1, dp) == 90);
}

字符串
我试图通过std::vector<int>& path作为tsp参数和工作时,我有更好的结果比当前的i节点path[i] = current,但它不工作.我认为它不工作,因为最好的结果在某个时刻不是最好的整个解决方案(例如,整个解决方案可能会访问节点在不同的顺序,他们不能被重新访问).
你可能想知道这段代码是如何检查节点是否被访问的。我通过位掩码来完成,过程可能看起来像这样(对于4个节点A,B,C,D):

main



预期路径为:0-1-3-2-0(成本等于90)
那么,在TSP问题中,如何跟踪给出最优解的路径呢?

ppcbkaq5

ppcbkaq51#

您可以添加一个参数(我们称之为link),用于收集给定掩码和节点的最佳邻居。因此它将具有与dp相同的类型。
然后在tsp运行之后,您可以使用此数据结构来重建路径。
下面是你的代码和必要的补充:

static int tsp(std::vector<std::vector<int>> const& dist, 
                size_t current, uint64_t mask, 
                std::vector<std::vector<int>>& dp,
                std::vector<std::vector<int>>& link) { // new parameter
    if (mask == ((size_t)1 << dist.size()) - 1)
        return dist[current][0];
    if (dp[mask][current] != -1)
        return dp[mask][current];

    int result = INT_MAX;
    for (uint64_t i = 0; i < dist.size(); ++i) {
        uint64_t val = (uint64_t)1 << i;
        if (mask & val)
            continue;

        // Pass extra argument in recursive call
        int sub = dist[current][i] + tsp(dist, i, mask | val, dp, link);
        if (sub < result) {
            result = sub;
            link[mask][current] = i; // Register which neighbor was best
        }
    }

    dp[mask][current] = result;
    return result;
}

// Function to translate `link` into a path, assuming 0 is starting node
static std::vector<int> getpath(std::vector<std::vector<int>>& link) {
    size_t current = 0;
    uint64_t mask = 0;
    std::vector<int> path;
    for (int i = 0; i < link[0].size(); i++) {
        path.push_back(current);
        mask |= 1 << current;
        current = link[mask][current];
    }
    return path;
}

int main() {
    std::vector<std::vector<int>> dist{
        {0, 10, 41, 31},
        {10, 0, 30, 19},
        {41, 30, 0, 20},
        {31, 19, 20, 0}
    };

    std::vector<std::vector<int>> dp(1 << dist.size(), std::vector<int>(dist.size(), -1));
   std::vector<std::vector<int>> link(1 << dist.size(), std::vector<int>(dist.size(), 0));

    int res  = tsp(dist, 0, 1, dp, link);
    std::vector<int> path = getpath(link);
    std::cout << "result " << res << "\n";
    for (auto i : path)
        std::cout << i << " ";
    std::cout << "\n";
}

字符串
请注意,您的代码假设0是起始节点(请参阅基本情况),因此我在getpath中使用了相同的假设。

相关问题