无向简单图的TSP算法
小规模精确解:
算法思想:
小规模精确解的算法中心思想是动态规划思想。
假设给定顶点集合V为{0,1,2,3,4,... .n}。由于图为无向完全图,我们可以很自然地将0视为输出的起点和终点。对于每个其他顶点i(除0之外),我们找到以0为起点,i为终点,且所有顶点恰好出现一次的最小成本路径。假设定义这条最小成本路径的成本为Cost(i),则相应TSP回路的Cost将是Cost(i)+ dist(i,0)其中dist(i,0)是从i到0的距离。最后,我们返回所有[cost(i)+ dist(i,0)]值,再从中选择最小值,此时该最小值也就是TSP的最优路径。而如何获得Cost(i)呢?那就是用动态规划算法来完成了:
定义C(S,i):
从集合S中访问每个顶点一次,且从0开始到i结束的最小成本路径的Cost值;
定义动态规划递归关系:
If size of S is 2, then S must be {0, i},
C(S, i) = dist(1, i)
Else if size of S is greater than 2.
C(S, i) = min { C(S-{i}, j) + dis(j, i)} where j belongs to S, j != i and j != 0.
所以,根据以上定义,求解TSP最优路径,等价于求解:
min {C(S, i) + dist(i, 0)} for any i in |V – 0|
伪代码构造:
TSP(V, G)
// input: V is the vertex set, exactly i indicates ith vertex
// input: G is a matrix, which G[i][j] indicates the distance from ith node to jth one
// output: the best optimal path of TSP
// output: the total cost of the path
// define C a matrix
// C[i][S]: C(S, i) be the cost of the minimum cost path visiting each vertex
// in set S exactly once, starting at 1 and ending at i
C(|V|, (2^|V|, -1))
// define path a matrix which the value records the last node of the current node
paths(|V|, 2^|V|)
min = INT_MAX
for e in V-{0} do
cost = dp_TSP(G, V, C, S, e, paths) + G[e][0]
if cost < min do
min = cost
index = e
endif
endfor
path = []
S = [all node in V]
do
push index in path
temp = index
index = path[index][S]
pop temp from S
while index != -1
push 0 in path
return path.reverse, min
dp_TSP(G, V, C, S, i, paths)
// input: G is a matrix, which G[i][j] indicates the distance from ith node to jth one
// input: V is verties set
// input: C means C(S, i)
// input: S includes all nodes needed to visited once and only once
// input: i, which indicate the end vertex in S
// input: records the last node of the current node in C(S, i)
// output: the value of C(S, i)
if |S| == 2
return G[0][i]
endif
if C(S, i) != -1
return C(S, i)
endif
min = INT_MAX
for j in S
if j != {0} and j != e then
value = C(S-{i}, j) + G[j][i]
if value <= min then
min = value
index = j
endif
endif
endfor
C(S, i) = min
path(S, i) = index
return min
函数设计:
邻接矩阵读取函数设计:
TSP函数设计:
dp_TSP函数设计:
测试结果:
算法分析:
由于我们使用动态编程解决了这个问题,且动态编程方法包含子问题,子问题如下图显示:
如果我们求解递归方程,我们将得到总 个子问题,也就是。每个子问题将花费O(n)时间(找到剩余(n-1)个节点的路径)。因此总时间复杂度为。空间复杂度也是子问题的数量,即。
虽然时间复杂度远小于O(n!),但仍然呈指数级。所需空间也是指数级的。因此,即使对于稍高数量的顶点,这种方法也是不可行的。由于在算法实现当中,用了long long int来作为S集合,所以超过64个节点的图就无法适用了。只能通过大规模近似求解。
完整代码:
#include <vector>
#include <iostream>
using namespace std;
typedef vector<vector<int> > G;
typedef vector<int> V;
G load_graph(int n);
void TSP(G& g, int n);
int dp_TSP(G& g, G& C, long long int S, int i, G& path);
V get_set(long long int S, int n);
int main(){
int n;
cin>> n;
G g = load_graph(n);
TSP(g, n);
return 0;
}
G load_graph(int n){
G g(n, V(n));
for(int a=0; a<n; a++)
for(int b=0; b<n; b++)
scanf("%d", &g[a][b]);
return g;
}
void printG(G& g){
int n = g.size();
int m = g[0].size();
for(int a=0; a<n; a++){
for(int b=0; b<m; b++)
cout<< g[a][b]<< ' ';
cout<< endl;
}
}
void printV(V g){
int n = g.size();
for(int a=0; a<n; a++)
cout<< g[a]<< ' ';
cout<< endl;
}
void TSP(G& g, int n){
// there are (2^n - 1) probable S for each i in C(S, i)
int m = (1<<n);
// define a term C(S, i) be the cost of the minimum cost path visiting each vertex
// in set S exactly once, starting at 1 and ending at i.
G C(n, V(m, -1));
// record the path
G path(n, V(m, -1));
// run the dynamic programming TSP algorithm
// start at 0th node, and end at 0th node
// find the minimum cost path with 1 as the starting point, i as the ending point
int min = INT_MAX;
int target = -1;
for(int i=1; i<n; i++){
// define S as a long long int variable, each ith bit record the existance of the ith node
// initial S = 1<<n - 1, which means that S includes all node at first
long long int S = (1<<n) - 1;
// the cost of corresponding Cycle would be cost(i) + dist(i, 0)
int cost = dp_TSP(g, C, S, i, path) + g[i][0];
// the minimum of all [cost(i) + dist(i, 0)] values
if(cost < min){
min = cost;
target = i;
}
}
V output;
long long int S = (1<<n) - 1;
do{
output.push_back(target);
int temp = target;
target = path[target][S];
S = S^(1<<temp);
}while(target != -1);
//printG(C);
cout<< min<< endl;
for(int a=n-1; a>=0; a--)
cout<< output[a]<< ' ';
cout<< 0<< endl;
}
int dp_TSP(G& g, G& C, long long int S, int i, G& path){
V set = get_set(S, g.size());
/*
If size of S is 2, then S must be {0, i},
C(S, i) = dist(1, i)
Else if size of S is greater than 2.
C(S, i) = min { C(S-{i}, j) + dis(j, i)} where j belongs to S, j != i and j != 0.
*/
if(set.size()==2){
path[i][S] = 0;
return g[0][i];
}
if(C[i][S] != -1)
return C[i][S];
int min = INT_MAX, index = -1;
long long int new_S = S^(1<<i);
for(int a=0; a<set.size(); a++){
int j = set[a];
if(j!=i && j!=0){
int result = dp_TSP(g, C, new_S, j, path) + g[j][i];
//cout<< i<< ' '<< result<< ' '<< j<< endl;
if(result < min){
min = result;
index = j;
}
}
}
C[i][S] = min;
path[i][S] = index;
//cout<< i<< ' '<< index<< "dad"<< endl;
return min;
}
V get_set(long long int S, int n){
V set;
for(int a=0; a<n; a++)
if(S & (1<<a))
set.push_back(a);
return set;
}