旅行商问题(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)是从i1的距离。最后,我们返回所有[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为根构造的MSTMST的前序遍历为1-2-4-3。最后添加1给出1-2-4-3-1,这是该算法的输出。

在这种情况下,近似算法产生最佳路径,但它可能无法在所有情况下产生最佳路径。这个算法如何近似?上述算法产生的输出路径绝不会超过最佳路径的两倍。让我们看看上述算法如何保证这一点。

在理解这个问题之前,我们首先要知道full walkfull 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-11-42-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(旅行商问题)相关推荐

  1. PAT甲级1150 Travelling Salesman Problem:[C++题解]旅行商问题、图论

    文章目录 题目分析 题目链接 题目分析 来源:acwing 分析: 旅行商问题:访问每个城市并回到原城市的最短路. 思路: 1)判断相邻两点有无距离(NA):2)每个点是否都能到:3)是否是回路:4) ...

  2. cf1504. Travelling Salesman Problem

    cf1504. Travelling Salesman Problem 题意: n个城市,编号1~n,每个城市有美丽值a[i],现在要从城市1出发,其他所有城市走一遍,最后回到城市1,城市i到j的花费 ...

  3. PAT 1150 Travelling Salesman Problem(25 分)- 甲级

    The "travelling salesman problem" asks the following question: "Given a list of citie ...

  4. 单目标应用:求解单仓库多旅行商问题(Single-Depot Multiple Travelling Salesman Problem, SD-MTSP)的人工兔优化算法ARO

    一.算法简介 人工兔优化算法(Artificial Rabbits Optimization ,ARO)由Liying Wang等人于2022年提出,该算法模拟了兔子的生存策略,包括绕道觅食和随机躲藏 ...

  5. 旅行商问题(Travelling salesman problem, TSP)

    旅行商问题建模与证明 – 个人学习记录

  6. 旅行商问题(travelling salesman problem, TSP) 解题报告

    旅行商问题是个熟知的问题.这次是因为coursera上面选的算法课而写代码实现.这里做个简单总结. 测试程序: 25 20833.3333 17100.0000 20900.0000 17066.66 ...

  7. 【HDU 5402】Travelling Salesman Problem(构造)

    被某题卡SB了,结果这题也没读好...以为每一个格子能够有负数就当搜索做了.怎么想也搜只是去,后来发现每一个格子是非负数,那么肯定就是构造题. 题解例如以下: 首先假设nn为奇数或者mm为奇数,那么显 ...

  8. 1150 Travelling Salesman Problem (25 分)【难度: 难 / 知识点: 图 模拟 未完成】

    https://pintia.cn/problem-sets/994805342720868352/problems/1038430013544464384

  9. 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开始经过每个点再 ...

  10. 多目标应用:多目标蜣螂优化算法求解多旅行商问题(Multiple Traveling Salesman Problem, MTSP)

    一.多旅行商问题 多旅行商问题(Multiple Traveling Salesman Problem, MTSP)是著名的旅行商问题(Traveling Salesman Problem, TSP) ...

最新文章

  1. 深度学习基础:张量运算
  2. [笔记]在ubuntu下使用conky
  3. python多个线程join_python-使用`thread.join()`时多线程冻结
  4. ML之sklearn:sklearn.metrics中常用的函数参数(比如confusion_matrix等 )解释及其用法说明之详细攻略
  5. [转] GDBT详解
  6. 发现 ASP.Net 的一个关于回车提交的 Bug ? 必须多于一个 Text 域回车提交,Server: ButtonX_Click 才能截获!...
  7. 通过这个免费的,由23部分组成的互动课程,学习ES6 +
  8. 动态规划之数字三角形问题
  9. 安卓开发笔记(十):升级ListView为RecylerView的使用
  10. python udp通信_Python网络编程(三)
  11. Java Bean 与Spring Bean 的区别
  12. 基于javaweb+jsp的健康体检信息管理系统(JavaWeb JSP MySQL Servlet SSM SpringBoot Bootstrap)
  13. VB/VBA的浮点数结构
  14. MPC控制笔记(一)
  15. 测试用例设计方法详解
  16. FineReport程序网络报表 - Hello,World
  17. R语言dplyr入门到进阶
  18. python打开xls_python读取XLS文件或CSV文件
  19. 帝国CMS7.2重置后台密码
  20. ThinkPad S5 升级安装Windows10后 连上网络后系统假死

热门文章

  1. 怎么学编程?学编程黄金4步,太多人卡在了第二步!
  2. 阿里云域名注册与免费证书申请
  3. 《富爸爸穷爸爸》读书笔记 -- 第一章
  4. ddl是什么意思网络语_ddl是什么
  5. Android adb命令开启飞行模式,开启wifi,开启移动数据等相关操作
  6. 目标检测经典论文——YOLOv1论文翻译(纯中文版):YOLO:统一的实时目标检测
  7. 荆棘鸟(The Thorn bird)
  8. 194.Vue.js智能扫码点餐系统(二十八)【支付宝支付流程、Nodejs支付源码解析、 实现支付功能(支付宝支付)】2019.04.01
  9. 扩展jQuery读书笔记:第一章,jQuery扩展
  10. 全智通A+常见问题汇总解答—A+配件仓库—维修领料—编辑领料单:最后一个仓库无法显示