双调TSP问题最牛逼的解法,不接受所有人反驳

ops/2024/10/17 21:59:57/

为什么我取这个标题呢?因为我的解 又简单 又好写

我找遍了许多答案,却没发现一个满意的,通过询问GPT,再通过自己的改善,总算得到正确的解了!!!

首先你得明白是如何递推的。

我们规定dp[i][j] 是分别以i,j为结尾的 TSP路线

然后要保证结点i和j为不同的边,简单认为为上下两条边就行了.

先给[0][1]初始化,我们需要知道如何递推

核心代码如下:

 for (int j = 2; j < n; ++j){for (int i = 0; i < j - 1; ++i){dp[i][j] = dp[i][j - 1] + euclidean_distance(points[j - 1], points[j]);// prt(dp, cnt);}double min_value = 1e9;for (int k = 0; k < j - 1; ++k){min_value = min(min_value, dp[k][j - 1] + euclidean_distance(points[k], points[j]));}dp[j - 1][j] = min_value;// prt(dp, cnt);}

然后你会发现 你一脸懵逼了。 这是在更新什么? 容我来解释一下

对j进行枚举,j是每次新加的点,从结点2开始。我们需要更新它相对于j-1 之外点的dp[i][j]。

你会疑惑,为什么j和j-1连体了?也就是说,我每次链接的是j-1和j这条边。因为我们需要让i和j保持为不同方向,让j-1和j保持同向,所以链接j-1和j。

然后再对j-1和j 找出最小的dp[j-1][j] 更新出所有边

但是这里要注意,这里的j-1和j 是不同的边,也就是说j-1和j 一个在上 一个在下。 为什么我们要这么写? 因为我们最后的答案,肯定是两个相邻的两个节点求最小值。因此这么更新。

然后在i-1里面的节点,取一个点,用来更新dp[k][j-1] 和dist(p[k],p[j]),让k和j同边了

最后找答案的过程 也比较好理解

 for (int i = 0; i < n - 1; ++i){result = min(result, dp[i][n - 1] + euclidean_distance(points[i], points[n - 1]));}

针对最后一个结点 进行枚举。从0结点开始枚举,链接该结点和最后结点的距离,然后再求dp[i][n-1] 得到答案

最终代码

#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
// 定义二维点
struct Point
{double x, y;
};// 计算两点之间的欧氏距离
double dist(Point a, Point b)
{return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}void prt(vector<vector<double>> dp, int n)
{for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){if (abs(dp[i][j] - 1e9) < 1e-7)cout << "0 ";elsecout << fixed << setprecision(2) << dp[i][j] << " ";}cout << endl;}
}
int main()
{int n;int cnt;cin >> n;cnt = n;vector<vector<double>> dp(n, vector<double>(n, 1e9)); // 初始化为一个很大的值// 输入点坐标vector<Point> p(n);for (int i = 0; i < n; ++i){cin >> p[i].x >> p[i].y;}// 按照 x 坐标从小到大排序sort(p.begin(), p.end(), [](Point a, Point b){ return a.x < b.x; });// 动态规划表// 初始状态:第一个点到第二个点的路径dp[0][1] = dist(p[0], p[1]);// 动态规划求解最短路径for (int j = 2; j < n; ++j){for (int i = 0; i < j - 1; ++i){dp[i][j] = dp[i][j - 1] + dist(p[j - 1], p[j]);// prt(dp, cnt);}double min_value = 1e9;for (int k = 0; k < j - 1; ++k){min_value = min(min_value, dp[k][j - 1] + dist(p[k], p[j]));}dp[j - 1][j] = min_value;// prt(dp, cnt);}// 最后求解从最右点回到最左点的最短路径double result = 1e9;for (int i = 0; i < n - 1; ++i){result = min(result, dp[i][n - 1] + dist(p[i], p[n - 1]));}// 输出结果,保留两位小数cout << fixed << setprecision(2) << result << endl;return 0;
}


http://www.ppmy.cn/ops/126309.html

相关文章

基础sql

在执行删除操作之前&#xff0c;建议先运行一个 SELECT 查询来确认你要删除的记录。这可以帮助你避免误删数据。 删除字段id默认值为空字符串的所有数据 delete from users where id ; 删除字段id默认值为null的所有数据 delete from users where id is null; 删除字段upd…

【firefox】火狐浏览器、火狐浏览器驱动、selenium版本号对应关系

火狐浏览器、火狐浏览器驱动、selenium版本号对应关系 链接地址:geckodriver、firefox、selenium版本号对应关系

【C语言】算术运算、关系运算、逻辑运算

算术运算&#xff1a;常见的数字运算&#xff0c;加减乘除等 关系运算&#xff1a;数值之间大小多少的关系 逻辑运算&#xff1a;逻辑与、或、非 #include <stdio.h> /* 功能&#xff1a;算术运算、关系运算、逻辑运算 时间&#xff1a;2024年10月 地点&#xff1a;贤者…

Wireshark数据包分析教程

Wireshark数据包分析教程 本教程将基于Wireshark工具捕获的数据包&#xff0c;逐步讲解网络数据帧中的各项信息&#xff0c;帮助你了解每个字段的含义及其作用。我们将从最基础的帧&#xff08;Frame&#xff09;信息开始&#xff0c;逐层解释包括以太网、IP、TCP、HTTP和JSON…

uniAPP如何开发?PHP语言的书写该如何制作

开发一个基于uni-app的项目以及与之交互的PHP后端涉及多个步骤和技术栈。以下是一个简要的指南&#xff0c;帮助你理解如何开始这两个部分的开发。 一、uni-app开发 1. 环境准备 Node.js&#xff1a;确保你已经安装了Node.js&#xff0c;这是构建和运行uni-app项目的基础。H…

Python爬虫:获取去哪儿网目的地下的评论数据

文章目录 1. 前言2. 分析网页页面的数据3. 代码实现1. 前言 本篇文章讲述如何使用Python爬虫爬取去哪儿目的地下的评论数据,会提供一些参考代码,需要完成的,可以私信,但是参考仅供学习使用喔,不能用于商业活动!读者切记。 用这个网页链接举例,链接为:https://travel.q…

SpringBoot2核心功能-web开发

目录 一、简单功能分析1.1、静态资源访问1.2、欢迎页支持、自定义 Favicon 二、请求参数处理2.1、请求映射2.1.1、rest使用与原理2.1.2、请求映射原理 2.2、普通参数与基本注解2.2.1、注解2.2.2、Servlet API&#xff1a;2.2.3、复杂参数&#xff1a; 三、拦截器四、Web原生组件…

【Shell】常见的 Shell 条件测试选项和控制命令的总结和整理

1. 字符串比较 -z "$var": 判断字符串是否为空&#xff0c;长度为 0 时返回 true。-n "$var": 判断字符串是否非空&#xff0c;长度大于 0 时返回 true。"$var" "$var2": 判断两个字符串是否相等。"$var" ! "$var2&q…