Travelling Salesman Problem(旅行商问题)
旅行商问题(TSP):给定一组城市和每对城市之间的距离,找到每个城市只访问一次并返回起点的最短路径。
注意Hamiltonian Cycle(哈密顿回路)和TSP之间的区别。 Hamiltoninan Cycle问题是找出是否存在一次访问每个城市一次的旅行。 在这里我们知道Hamiltonian Cycle存在(因为graph
是完整的)并且实际上存在许多这样的Cycle,问题是找到最小权重的Hamiltonian Cycle。
例如,请考虑下图中所示的graph
。 图中的TSP路径是1-2-4-3-1
。 这次旅行的费用是10 + 25 + 30 + 15
,即80
。
这是一个非常著名的NP难题。 对于这个问题,不存在多项式时间复杂度的解法。以下是旅行商问题的不同解决方案。
朴素的解决方案:
- 1)将城市1视为起点和终点。
- 2)生成所有
(n-1)!
城市的排列。 - 3)计算每个排列的路径长度并跟踪最小路径长度的排列。
- 4)返回最后的最小路径排列。
// implementation of traveling Salesman Problem
int travllingSalesmanProblem(int graph[][V], int s)
{ // store all vertex apart from source vertex vector<int> vertex; for (int i = 0; i < V; i++) if (i != s) vertex.push_back(i); // store minimum weight Hamiltonian Cycle. int min_path = INT_MAX; do { // store current Path weight(cost) int current_pathweight = 0; // compute current path weight int k = s; for (int i = 0; i < vertex.size(); i++) { current_pathweight += graph[k][vertex[i]]; k = vertex[i]; } current_pathweight += graph[k][s]; // update minimum min_path = min(min_path, current_pathweight); } while (next_permutation(vertex.begin(), vertex.end())); return min_path;
}
这个解法在之前的Hamiltoninan Cycle中也提到过,对于较大的n
来说,这种做法当然是不可取的。这个问题可以通过动态规划的方法来解决。
对于给定的顶点集合为{1,2,3,4 ......}
。我们将1
视为输出的起点和终点。对于每个其他顶点i
(除1
之外),我们找到一条到i
的最短路径,其中1
为起点,i
为结束点,所有顶点恰好出现一次。假设这条路径的总长度为cost(i)
,则我们最后的路径长度将是cost(i)+dist(i,1)
,其中dist(i,1)
是从i
到1
的距离。最后,我们返回所有[cost(i)+dist(i,1)]
值的最小值。到目前为止一切都看起来很简单,现在的问题就变成了如何获得cost(i)
?
我们可以使用动态规划来计算cost(i)
。首先我们定义C(S,i)C(S,i)C(S,i)表示对于集合SSS中每个顶点i
来说,从1
开始到i
结束的最短路径长度。我们从大小为2
的所有子集开始,并计算所有子集的C(S,i)C(S,i)C(S,i),其中SSS是子集,然后我们计算大小为3
的所有子集SSS的C(S,i)C(S,i)C(S,i),依此类推。请注意,每个子集中必须存在1
。
如果SSS的大小为2,那么SSS必须是{1,i}\{1,i\}{1,i},C(S,i)=dist(1,i)C(S, i) = dist(1, i)C(S,i)=dist(1,i)
否则,如果SSS的大小大于2,C(S,i)=min{C(S−i,j)+dist(j,i)}C(S, i) = min \{C(S - {i}, j) + dist(j, i)\}C(S,i)=min{C(S−i,j)+dist(j,i)}其中jjj属于SSS,j≠ij \neq ij̸=i且j≠1j \neq 1j̸=1。
对于一组大小为n
的集合,我们考虑n-2
个子集,每个子集的大小为n-1
,使得所有子集中都没有第n
个数。
使用上述递归关系,我们就可以写出相应的动态规划的代码。最多有O(n∗2n)O(n*2^n)O(n∗2n)个子问题,每个子问题需要线性时间来解决。因此总运行时间为O(n2∗2n)O(n^2*2^n)O(n2∗2n)。时间复杂度远小于O(n!)O(n!)O(n!),但仍然呈指数级。所需空间也是指数级的。因此,即使对于稍高数量的顶点,这种方法也是不可行的。
int n=4;
int dist[10][10] = {{0,20,42,25},{20,0,30,34},{42,30,0,10},{25,34,10,0}
};
int VISITED_ALL = (1 << n) -1;
int dp[16][4];
int cost(int mask,int pos)
{if (mask == VISITED_ALL){return dist[pos][0];}if (dp[mask][pos] != -1){return dp[mask][pos];}//Now from current node, we will try to go to every other node and take the min ansint ans = INT_MAX;//Visit all the unvisited cities and take the best routefor (int city = 0; city < n; city++){if ((mask & (1 << city)) == 0){int newAns = dist[pos][city] + cost(mask | ( 1 << city), city);ans = min(ans, newAns);}}return dp[mask][pos] = ans;
}
我们稍微解释一下上面这个代码,其中mask
表示我们访问的节点,假设我们有4
各节点,如果mask=1111
的话,表示cdba
都访问过了,也就是mask=1<<4 - 1=1111
;如果mask=0001
,表示只有a
访问过了,以此类推。那么我们就可以通过(mask & (1 << city)) == 0
判断当前节点是不是被访问过。例如,对于节点a
city = 0
1 << 0 = 0001
mask = 0001
mask & (1 << city) == true
如果当前的节点没有被访问过,我们才进行后续操作。也就是递归调用cost
。其中mask | ( 1 << city)
表示对city
进行访问,例如
city = 0
1 << 0 = 0001
mask = 0000
mask | (1 << city) = 0001
最后我们只要取每次结果的最小值即可。
前面已经说过,这个问题没有多项式时间的解决方法。但是有一些近似的算法来解决这个问题。仅当问题实例满足Triangle-Inequality(三角不等式)时,近似算法才有效。
三角不等式:从i
到达顶点j
的最远路径总是直接从i
到达j
,而不是通过其他一些顶点k
(或顶点),即dist(i,j)
总是小于或等于到dist(i,k) + dist(k,j)
。三角不等式在许多实际情况中都有。
当cost
函数满足三角不等式时,我们可以为TSP设计一个近似算法,该算法返回一个最短路径长度不超过最优解的两倍。我们的想法是使用最小生成树(MST)。以下是基于MST的算法。
- 1)以
1
为起点和终点。 - 2)使用
Prim
算法,以1
为根构造MST
。 - 3)列出在构造的
MST
的前序步行中访问的顶点,并在末尾添加1
。
考虑以下示例。图中显示了以1
为根构造的MST。 MST的前序遍历为1-2-4-3
。最后添加1
给出1-2-4-3-1
,这是该算法的输出。
在这种情况下,近似算法产生最佳路径,但它可能无法在所有情况下产生最佳路径。这个算法如何近似?上述算法产生的输出路径绝不会超过最佳路径的两倍。让我们看看上述算法如何保证这一点。
在理解这个问题之前,我们首先要知道full walk。full walk是指在先序遍历访问树的全部节点时列出的所有走的步骤。上面树的full walk将是1-2-1-4-1-3-1
。
- 1)TSP绝不低于MST。 (MST的定义是,它是连接所有顶点的最小生成树)。
- 2)full walk的最多是MST的两倍(MST的每个边缘最多访问两次)
- 3)上述算法的输出小于full walk。在上面的算法中,我们打印前序遍历作为输出。在前序遍历中,full walk的两个或更多个边缘被单个边缘替换。例如,
2-1
和1-4
被2-4
代替。因此,如果图形遵循三角形不等式,那么这总是正确的。
从上述三个陈述中,我们可以得出结论,近似算法产生的输出结果绝不会超过最佳解决方案的两倍。
我们已经讨论了一个非常简单的2倍近似算法来解决旅行商问题。对于该问题还有其他更好的近似算法。例如,Christofides
算法是1.5倍近似算法。
reference:
https://www.geeksforgeeks.org/travelling-salesman-problem-set-1/
https://www.geeksforgeeks.org/travelling-salesman-problem-set-2-approximate-using-mst/
https://www.geeksforgeeks.org/traveling-salesman-problem-tsp-implementation/
https://codingblocks.com/resources/travelling-salesman/
Travelling Salesman Problem(旅行商问题)相关推荐
- PAT甲级1150 Travelling Salesman Problem:[C++题解]旅行商问题、图论
文章目录 题目分析 题目链接 题目分析 来源:acwing 分析: 旅行商问题:访问每个城市并回到原城市的最短路. 思路: 1)判断相邻两点有无距离(NA):2)每个点是否都能到:3)是否是回路:4) ...
- cf1504. Travelling Salesman Problem
cf1504. Travelling Salesman Problem 题意: n个城市,编号1~n,每个城市有美丽值a[i],现在要从城市1出发,其他所有城市走一遍,最后回到城市1,城市i到j的花费 ...
- PAT 1150 Travelling Salesman Problem(25 分)- 甲级
The "travelling salesman problem" asks the following question: "Given a list of citie ...
- 单目标应用:求解单仓库多旅行商问题(Single-Depot Multiple Travelling Salesman Problem, SD-MTSP)的人工兔优化算法ARO
一.算法简介 人工兔优化算法(Artificial Rabbits Optimization ,ARO)由Liying Wang等人于2022年提出,该算法模拟了兔子的生存策略,包括绕道觅食和随机躲藏 ...
- 旅行商问题(Travelling salesman problem, TSP)
旅行商问题建模与证明 – 个人学习记录
- 旅行商问题(travelling salesman problem, TSP) 解题报告
旅行商问题是个熟知的问题.这次是因为coursera上面选的算法课而写代码实现.这里做个简单总结. 测试程序: 25 20833.3333 17100.0000 20900.0000 17066.66 ...
- 【HDU 5402】Travelling Salesman Problem(构造)
被某题卡SB了,结果这题也没读好...以为每一个格子能够有负数就当搜索做了.怎么想也搜只是去,后来发现每一个格子是非负数,那么肯定就是构造题. 题解例如以下: 首先假设nn为奇数或者mm为奇数,那么显 ...
- 1150 Travelling Salesman Problem (25 分)【难度: 难 / 知识点: 图 模拟 未完成】
https://pintia.cn/problem-sets/994805342720868352/problems/1038430013544464384
- Codeforces Round #712 (Div. 2) E. Travelling Salesman Problem 思维转换
传送门 文章目录 题意: 思路: 题意: 给你nnn个点,从iii到jjj的花费是max(ci,aj−ai)max(c_i,a_j-a_i)max(ci,aj−ai),求从111开始经过每个点再 ...
- 多目标应用:多目标蜣螂优化算法求解多旅行商问题(Multiple Traveling Salesman Problem, MTSP)
一.多旅行商问题 多旅行商问题(Multiple Traveling Salesman Problem, MTSP)是著名的旅行商问题(Traveling Salesman Problem, TSP) ...
最新文章
- 深度学习基础:张量运算
- [笔记]在ubuntu下使用conky
- python多个线程join_python-使用`thread.join()`时多线程冻结
- ML之sklearn:sklearn.metrics中常用的函数参数(比如confusion_matrix等 )解释及其用法说明之详细攻略
- [转] GDBT详解
- 发现 ASP.Net 的一个关于回车提交的 Bug ? 必须多于一个 Text 域回车提交,Server: ButtonX_Click 才能截获!...
- 通过这个免费的,由23部分组成的互动课程,学习ES6 +
- 动态规划之数字三角形问题
- 安卓开发笔记(十):升级ListView为RecylerView的使用
- python udp通信_Python网络编程(三)
- Java Bean 与Spring Bean 的区别
- 基于javaweb+jsp的健康体检信息管理系统(JavaWeb JSP MySQL Servlet SSM SpringBoot Bootstrap)
- VB/VBA的浮点数结构
- MPC控制笔记(一)
- 测试用例设计方法详解
- FineReport程序网络报表 - Hello,World
- R语言dplyr入门到进阶
- python打开xls_python读取XLS文件或CSV文件
- 帝国CMS7.2重置后台密码
- ThinkPad S5 升级安装Windows10后 连上网络后系统假死
热门文章
- 怎么学编程?学编程黄金4步,太多人卡在了第二步!
- 阿里云域名注册与免费证书申请
- 《富爸爸穷爸爸》读书笔记 -- 第一章
- ddl是什么意思网络语_ddl是什么
- Android adb命令开启飞行模式,开启wifi,开启移动数据等相关操作
- 目标检测经典论文——YOLOv1论文翻译(纯中文版):YOLO:统一的实时目标检测
- 荆棘鸟(The Thorn bird)
- 194.Vue.js智能扫码点餐系统(二十八)【支付宝支付流程、Nodejs支付源码解析、 实现支付功能(支付宝支付)】2019.04.01
- 扩展jQuery读书笔记:第一章,jQuery扩展
- 全智通A+常见问题汇总解答—A+配件仓库—维修领料—编辑领料单:最后一个仓库无法显示