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)。