TSP 问题是旅行商从一个城市出发,各个城市仅经过一次,最后回到出发的城市,求出最短的路径的距离。使用动态规划解决问题,首先需要证明问题具有最优子结构性质。可以使用反证法证明TSP 问题具有最优子结构性质。

1. 问题假设

假设有4个城市,编号分别为0、1、2、3。设distance(k,V')  且V' = V-k 表示从顶点i出发,经过V'集合中的每个点,并且只经过一次最后回到出发点i的最短距离。所以可以有下面的表达式:

d(k,{}) = cki  以及d(i,V') = min(cik+d(k,V'-{k}))  (k 属于V')

2. 算法描述

首先使用n*n的二维矩阵表示城市之间的距离,如1行2列表示城市1到城市2的距离。将城市到自己的距离设为最大值。使用另一个矩阵,行表示各个城市,列表示城市集合的各个子集,按照子集的大小排列。求解某个点开始经过集合中的所有的点一次的最短距离可以通过比较各个自问题获得。即:distance(i,V') = min(cik,distance(k,V''))  其中k 为V' 集合中任意取出的一个顶点,V'表示从总集合中减去i点的集合,V'' 表示在V' 的基础上减去取出的顶点k的集合。

3. 实现代码

代码实现中,在集合的表示与子集的获得上遇到了一些问题。之后先通过全排列获得所有的组合,之后再全排列中加上判断,即当前附加的顶点的编号必须大于前面的编号,最后就获得了某个集合的所有的子集。之后再对子集拍一下序就可以了。下面是获得所有子集的方法:

 void getSubsets(deque<int> &seq,bool *visited,int depth,int n) {if (depth == 0) {subsets.push_back(deque<int>()); //加入空集合getSubsets(seq, visited, depth + 1, n);}else if (depth >= n) {//已经达到了最大的深度return;}else {for (int i = 1; i < n; ++i) {//判断是否访问过,比较大小,防止出现顺序不同但集合元素相同的情况if (!visited[i]) {if (!seq.empty() && i < seq.back())continue;visited[i] = true;seq.push_back(i);subsets.push_back(*new deque<int>(seq));   //保存当前的序列getSubsets(seq, visited, depth + 1, n);visited[i] = false;seq.pop_back();}}}}

下面是具体的实现代码。在求解的前面的各个阶段,集合中没有包括起始顶点v0,直到最后才求解distance(v0,V)。

#include <deque>
#include <iostream>
#include <cmath>
#include <fstream>
#include <algorithm>using namespace std;class Solution {
private:/***    distance 为该结构体的二维矩阵*    用于保存每个子问题的最短路径以及依赖的前一个子问题在distance 矩阵中的坐标*/struct distanceEle {int minVal;int prei;int prej;distanceEle() :minVal(0), prei(0), prej(0) {}};/*** seq 当前的序列*  visited 标识当前访问过的元素* depth 当前的深度 从0开始*   n 最大的深度*/void getSubsets(deque<int> &seq,bool *visited,int depth,int n) {if (depth == 0) {subsets.push_back(deque<int>());    //加入空集合getSubsets(seq, visited, depth + 1, n);}else if (depth >= n) {//已经达到了最大的深度return;}else {for (int i = 1; i < n; ++i) {//判断是否访问过,比较大小,防止出现顺序不同但集合元素相同的情况if (!visited[i]) {if (!seq.empty() && i < seq.back())continue;visited[i] = true;seq.push_back(i);subsets.push_back(*new deque<int>(seq));   //保存当前的序列getSubsets(seq, visited, depth + 1, n);visited[i] = false;seq.pop_back();}}}}static bool cmp(const deque<int> &obj1, const deque<int> &obj2) {if (obj1.size() < obj2.size())return true;elsereturn false;}/***    用于求出某个集合关于总的集合的补集*/deque<int> getsupplementSet(deque<int> &fullSet, deque<int> &subset) {//首先对集合排序sort(fullSet.begin(), fullSet.end());sort(subset.begin(), subset.end());deque<int>::iterator iterFull = fullSet.begin(), iterSub = subset.begin();deque<int> result;while (iterSub != subset.end()) {if (*iterFull < *iterSub) {result.push_back(*iterFull);++iterFull;}else {++iterFull, ++iterSub;}}while (iterFull != fullSet.end()) {result.push_back(*iterFull);++iterFull;}return result;}/*** 在subsets 中定位subset 的坐标*/int locateSubsetIndex(deque<int> subset) {bool flag = true;for (int i = 0; i < subsets.size(); ++i) {if (subset.size() != subsets[i].size())continue;flag = true;for (int j = 0; j < subsets[i].size(); ++j) {if (subsets[i][j] != subset[j])flag = false;}if (flag == true)return i;}return -1;}/***  依次解决子问题*/void dynamicProgram(int n,deque<int> fullset) {deque<int> supplementSet;for (int i = 1; i < subsets.size(); ++i) {supplementSet = getsupplementSet(fullset, subsets[i]);    //获取当前子集的补集for (auto ele : supplementSet) {     //对补集中的每一个元素进行操作int tmp;deque<int> tmpSet;distance[ele][locateSubsetIndex(subsets[i])].minVal = INT_MAX; //先初始化一个大的值for (int j = 0; j < subsets[i].size(); ++j) {tmpSet = deque<int>(subsets[i]);tmp = tmpSet[j];tmpSet.erase(tmpSet.begin() + j);//记录基于前面的子问题的最短路径if (arc[ele][tmp] + distance[tmp][locateSubsetIndex(tmpSet)].minVal < distance[ele][locateSubsetIndex(subsets[i])].minVal) {distance[ele][locateSubsetIndex(subsets[i])].minVal = arc[ele][tmp] + distance[tmp][locateSubsetIndex(tmpSet)].minVal;distance[ele][locateSubsetIndex(subsets[i])].prei = tmp;distance[ele][locateSubsetIndex(subsets[i])].prej = locateSubsetIndex(tmpSet);}}}}}/***  处理动态规划的最后一个阶段*  distanceColNum 表示distance 的列的个数*/void endProgram(deque<int> fullSet,int distanceColNum) {int tmp;deque<int> tmpSet;distance[0][distanceColNum - 1].minVal = INT_MAX;for (int i = 0; i < fullSet.size(); ++i) {tmpSet = deque<int>(fullSet);tmp = tmpSet[i];tmpSet.erase(tmpSet.begin() + i);if (arc[0][tmp] + distance[tmp][locateSubsetIndex(tmpSet)].minVal< distance[0][distanceColNum - 1].minVal) {distance[0][distanceColNum - 1].minVal = arc[0][tmp] + distance[tmp][locateSubsetIndex(tmpSet)].minVal;distance[0][distanceColNum - 1].prei = tmp;distance[0][distanceColNum - 1].prej = locateSubsetIndex(tmpSet);}}}public:deque<deque<int>> subsets; //所有的子集的集合int **arc;    //带权的矩阵distanceEle **distance;  //动态规划中的填表void findShortest() {ifstream infile("input.txt");int n = 0;infile >> n;bool *visited = new bool[n];for (int i = 0; i < n; ++i)visited[i] = false;deque<int> seq;getSubsets(seq, visited, 0, n);sort(subsets.begin(), subsets.end(), &Solution::cmp);  //对子集进行排序//读取带权图的矩阵int tmp = 0;arc = new int*[n];for (int i = 0; i < n; ++i) {arc[i] = new int[n];for (int j = 0; j < n; ++j) {infile >> tmp;if (tmp == -1)tmp = INT_MAX;arc[i][j] = tmp;}}infile.close();//测试cout << "遍历arc :" << endl;for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {cout << arc[i][j] << "  ";}cout << endl;}cout << "遍历子集:" << endl;for (int i = 0; i < subsets.size(); ++i) {for (auto ele : subsets[i])cout << ele << "  ";cout << endl;}deque<int> fullSet;for (int i = 1; i < n; ++i)fullSet.push_back(i);//求取各个子集的补集cout << "对应的补集为:" << endl;for (int j = 0; j < subsets.size(); ++j) {for (auto ele : getsupplementSet(fullSet, subsets.at(j))) {cout << ele << "  ";}cout << endl;}/////初始化distancedistance = new distanceEle*[n];for (int i = 0; i < n; ++i) {distance[i] = new distanceEle[subsets.size()];}//初始化distance 第0列,1~n-1行for (int i = 1; i < n; ++i)distance[i][0].minVal = arc[i][0];//对求出来的各个子集,按照大小,从空集合开始动态规划dynamicProgram(n, fullSet);endProgram(fullSet, subsets.size());//测试输出distancecout << "遍历distance :" << endl;for (int i = 0; i < n; ++i) {for (int j = 0; j < subsets.size(); ++j) {cout << "(" << distance[i][j].minVal << ", " << distance[i][j].prei << ", " << distance[i][j].prej<< ")   ";}cout << endl;}}/*** 输出最短路径*/void outputShortestPath(int distanceColNum) {int indexi = 0, indexj = distanceColNum - 1;int i, j;while (indexj > 0) {cout << indexi << "---->";i = distance[indexi][indexj].prei;j = distance[indexi][indexj].prej;indexi = i;indexj = j;}cout << indexi << endl;}};int main()
{Solution solution;solution.findShortest();solution.outputShortestPath(solution.subsets.size());return 0;
}

程序使用的输入文件的内容:

运行的截图:

参考:《算法设计与实现》(第二版)

动态规划 TSP 问题相关推荐

  1. 算法复习第四章动态规划

    算法复习第四章动态规划 动态规划 TSP问题 0-1bag 动态规划 TSP问题 0-1bag 最长公共子序列不考:

  2. python用动态规划求删除路径_Python | 动态规划求解TSP

    解题思路主要有两部分: i为当前节点(城市),S为还没有遍历的节点(城市集合),表示从第i个节点起,经历S集合中所有的点,到达终点的最短路径长度. 回溯找到最优的路径,需要将S集合一一对应一个数字(类 ...

  3. 求解TSP问题(python)(穷举、最近邻居法、opt-2法、动态规划、插入法)

    TSP问题(python) 排序问题 读取文件格式:第一行为城市数目,剩余行为各城市坐标 (1) 城市全排列,在所有解决方案中选择最好的一个(解决20个城市的时候会有困难了(见维基百科)) # 生成全 ...

  4. Solve TSP with dynamic programming——动态规划解决旅行商(邮递员)问题

    无向简单图的TSP算法 小规模精确解: 算法思想: 小规模精确解的算法中心思想是动态规划思想. 假设给定顶点集合V为{0,1,2,3,4,... .n}.由于图为无向完全图,我们可以很自然地将0视为输 ...

  5. 动态规划经典问题--TSP问题

    Travelling Salesman Problem 旅行商问题,即TSP问题(Travelling Salesman Problem)又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假 ...

  6. TSP之动态规划找最优解

    动态规划解决TSP问题     假设p0,p1,p2...pn-1是从p0到pn-1的最短路径,则p1,p2...pn-1也是最短路径.     pi:点i     d(i,v):从i经过点集合v所有 ...

  7. 商旅问题TSP——动态规划(c++ 动态规划)

    1.定义 TSP问题(旅行商问题)是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次然后回到出发城市,并要求所走的路程最短. 本文以2019字节跳动春招笔试第5题,毕业旅行问题为例: 小明目前在做 ...

  8. 蚁群算法解决TSP问题(2#JAVA代码+详细注释+对比动态规划【JAVA】)

    第一部分:原理 TSP10cities.txt 1 2066 2333 2 935 1304 3 1270 200 4 1389 700 5 984 2810 6 2253 478 7 949 302 ...

  9. tsp问题动态规划python_用Python解决TSP问题(2)——动态规划算法

    本介绍用python解决TSP问题的第二个方法--动态规划法 算法介绍 动态规划算法根据的原理是,可以将原问题细分为规模更小的子问题,并且原问题的最优解中包含了子问题的最优解.也就是说,动态规划是一种 ...

  10. 动态规划之TSP(Travel Salesman Problem)算法

    旅行商问题(Traveling Saleman Problem,TSP)又译为旅行推销员问题.货郎担问题,简称为TSP问题,是最基本的路线问题,该问题是在寻求单一旅行者由起点出发,通过所有给定的需求点 ...

最新文章

  1. unity课设小游戏_Unity制作20个迷你小游戏实例训练视频教程
  2. ubuntu 14.04安装postgresql最新版本
  3. 【深度学习】我用 PyTorch 复现了 LeNet-5 神经网络(自定义数据集篇)!
  4. js 处理十万条数据_Python数据可视化2018:为什么这么多的库?
  5. java signed_如何从java中的字节读取signed int?
  6. mysql中timestamp,datetime,int类型的区别与优劣
  7. 20. Cookie 和 Session
  8. Windows文本文件编码
  9. 单例模式-1.单利模式的简单实现
  10. 递归法:求n个元素的全排列
  11. 多样化实现Windows Phone 7本地数据库访问1
  12. 浙江富商的24条至理经验
  13. Qt组态软件设计文章导航
  14. 人工智能 一种现代方法 第2章 智能化智能体
  15. 解决无法删除文件:无法读取源文件或磁盘
  16. 今天中午吃什么转盘html,吃到撑的几种简单午饭,让你再也不发愁吃什么了!...
  17. 安卓仿苹果音量调节_安卓不仿苹果静音键?千万别小瞧“静音键”, 功能强悍到无敌!...
  18. 解决errno 256报错_wuli大世界_新浪博客
  19. spark ml pipelines
  20. delphi SysErrorMessage 函数和系统错误信息表 good

热门文章

  1. (池州市地图)行政区划图高清矢量cdr|pdf(详细版2021年)
  2. 室内设计数据手册pdf_室内设计资料集pdf下载-室内设计资料集电子版pdf高清扫描版-东坡下载...
  3. 雨林木风SP3YN9.9 装机版09年09月更新(终结版)
  4. 雨林木风Ghost XP SP3 装机版 YN9.9 九月修正版 【雪豹】
  5. 盈透IBKR IBAPI Quant | Database | 通过盈透ibapi下载历史数据 Part 01
  6. Apple Pay发展与安全
  7. SpringBoot+websocket 实现web聊天功能(单聊、保存消息)
  8. BASIC语言、FreeBasic语言
  9. 那些年,我们一起追过的seo培训!
  10. 对《Head First Java》读者的一些建议