引用之前ACM博客状压DP一文对于TSP问题的描述:
经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需经过所有城市后,回到起点。应该如何选择路线,使得总行程距离最短。从图论的观点来看是,该问题实质是在一个有权完全无向图中找一个权值最小的哈密顿回路。
大部分TSP问题都是NP-hard问题,即没有多项式时间复杂度的算法,一般倾向于剪枝搜索或者状压DP的方式解决。这类问题地点数量通常很小。
以上主要考虑的是TSP问题精确解的求法,在数模中我们可以求近似解,从中筛选较优解。
DP
首先回顾一下之前状压DP的方法:
定义状态:
f
[
s
t
a
t
u
s
]
[
j
]
:表示状态为
s
t
a
t
u
s
(即已经过
s
t
a
t
u
s
所表示点集),最后访问的是节点
j
的最短距离
f[status][j]: 表示状态为 status (即已经过 status 所表示点集),最后访问的是节点 j 的最短距离
f[status][j]:表示状态为status(即已经过status所表示点集),最后访问的是节点j的最短距离
递推方程:
f
[
s
t
a
t
u
s
]
[
j
]
=
m
i
n
k
∈
s
t
a
t
u
s
&
&
j
∉
s
t
a
t
u
s
(
f
[
s
t
a
t
u
s
−
(
1
<
<
j
)
]
[
k
]
+
d
i
s
[
k
]
[
j
]
)
f[status][j] = min_{k{\in}status\&\&j{\notin}status}(f[status-(1<<j)][k]+dis[k][j])
f[status][j]=mink∈status&&j∈/status(f[status−(1<<j)][k]+dis[k][j])
也可以写作:
f
[
s
t
a
t
u
s
∣
(
1
<
<
k
)
]
[
k
]
=
m
i
n
k
∉
s
t
a
t
u
s
(
f
[
s
t
a
t
u
s
]
[
j
]
+
d
i
s
[
j
]
[
k
]
)
f[status | (1 << k)][k] = min_{k{\notin}status}(f[status][j] + dis[j][k])
f[status∣(1<<k)][k]=mink∈/status(f[status][j]+dis[j][k])
最终:
a
n
s
=
m
i
n
(
f
[
(
1
<
<
n
)
−
1
]
[
i
]
+
d
i
s
[
i
]
[
s
t
]
)
ans=min(f[(1<<n)-1][i]+dis[i][st])
ans=min(f[(1<<n)−1][i]+dis[i][st])
关键点:1. 状态压缩已访问节点,实际上可以直接用数组表示集合,这里通过状压的方法用一个数表示集合 2. 转移状态由已访问节点集合以及最后访问节点确定
C++程序实现:
// 参照第二个递推方程
for (int i = 1; i < (1 << n); i++) { // i即是status,枚举状态
for (int j = 0; j < n; j++) { // 枚举
if (!((i >> j) & 1)) continue; // 枚举状态f[i]可能的最后访问节点
for (int k = 0; k < n; k++) { // j -> k,k为状态f[i|(1<<k)]最后访问节点
if ((i >> k) & 1) continue;
f[i | (1 << k)][k] = min(f[i | (1 << k)][k], f[i][j] + g[j + 1][k + 1]);
}
}
}
时间复杂度: O ( n 2 ∗ 2 n ) O(n^2 * 2^n) O(n2∗2n)
一个经典的近似解做法
在 P ≠ N P P\neq{NP} P=NP情况下TSP问题不存在多项式时间的常数近似。但是,TSP问题在度量空间是可以有多项式时间的常数近似。所谓度量空间,就是空间中的任意3点满足三角不等式。
- 求出图G的最小生成树T,其权值和为 W ( T ) W(T) W(T);
- 将T的边double一下得到欧拉图G’(每个点的度数均为偶数);
- 求G’欧拉回路,根据得到的点序列,跳过重复的点便得到一个哈密顿回路C。
因为满足三角不等式,所以 W ( C ) ≤ 2 W ( T ) W(C)\leq2W(T) W(C)≤2W(T) ,而 W ( T ) ≤ O P T W(T){\leq}OPT W(T)≤OPT ,所以 W ( C ) ≤ 2 O P T W(C)\leq2OPT W(C)≤2OPT ,这意味着该近似解不会超过最优解的2倍。
我们发现:由于G’是一棵树的边double而得,对G’进行dfs遍历,递归时走原边,回溯时走复制边,跳过重复的点得到实际就是树的前/中/后序序列。
求最小生成树T:
求前序序列L:
时间复杂度:通过类似Dijkstra算法中用堆存储所有边,依次弹出所有边并判断一下是否是由已访问点连向未访问点的,可以保证所有边访问一次,这样的时间复杂度为 O ( m l o g m ) O(mlogm) O(mlogm) ,而 1 / 2 n ∗ n ≥ m 1/2n*n\geq{m} 1/2n∗n≥m ,因此可以表示为 O ( n 3 l o g n ) O(n^3logn) O(n3logn) 。
欧拉回路
定义及性质参考:https://zhuanlan.zhihu.com/p/44055074
求欧拉回路的Fleury算法:
设G为一无向欧拉图,
(1)任取G中一顶点
v
0
v_0
v0,令
p
0
=
v
0
p_0=v_0
p0=v0;
(2)假设沿
p
i
=
v
0
e
1
v
1
e
2
v
2
.
.
.
e
i
v
i
p_i=v_0e_1v_1e_2v_2...e_iv_i
pi=v0e1v1e2v2...eivi走到顶点
v
i
v_i
vi,按下面方法从
E
(
G
)
−
{
e
0
,
e
1
,
e
2
,
.
.
.
e
i
}
E(G)-\{e_0,e_1,e_2,...e_i\}
E(G)−{e0,e1,e2,...ei}中选择
e
i
+
1
e_{i+1}
ei+1:
(a)
e
i
+
1
e_{i+1}
ei+1与
v
i
v_{i}
vi关联;
(b)除非无别的边供选择,否则
e
i
+
1
e_{i+1}
ei+1不应该是
E
(
G
)
−
{
e
0
,
e
1
,
e
2
,
.
.
.
e
i
}
E(G)-\{e_0,e_1,e_2,...e_i\}
E(G)−{e0,e1,e2,...ei}中的桥。
(3)(2)不能进行时算法停止。
割边:设无向图
G
(
V
,
E
)
G(V, E)
G(V,E)为连通图,若边集
E
1
⊆
E
E_1⊆E
E1⊆E,在图G 中删除
E
1
E_1
E1中所有的边后得到的子图是不连
通的,而删除了
E
1
E_1
E1的任一真子集后得到的子图是连通图,则称
E
1
E_1
E1是G的一个割边集。若一条边
构成一个割边集,则称该边为割边,或桥。
修改圈近似算法
首先求一个 Hamilton 圈 C ,然后适当修改 C 以得到具有更小权的新 Hamilton 圈。修改的方法为改良圈算法。设初始圈为
C
=
v
1
v
2
.
.
.
v
n
v
1
C = v_1v_2...v_nv_1
C=v1v2...vnv1 。
(1)对于
1
≤
i
<
i
+
1
<
j
≤
n
1\leq{i}<i+1<j\leq{n}
1≤i<i+1<j≤n ,构造新的 Hamilton 圈
C
i
j
=
v
1
v
2
.
.
.
v
i
v
j
v
j
−
1
v
j
−
2
.
.
.
v
i
+
1
v
j
+
1
v
j
+
2
.
.
.
v
n
v
1
C_{ij}=v_1v_2...v_iv_jv_{j-1}v_{j-2}...v_{i+1}v_{j+1}v_{j+2}...v_nv_1
Cij=v1v2...vivjvj−1vj−2...vi+1vj+1vj+2...vnv1
若
w
(
v
i
v
j
)
+
w
(
v
i
+
1
v
j
+
1
)
<
w
(
v
i
v
i
+
1
)
+
w
(
v
j
v
j
+
1
)
w(v_iv_j)+w(v_{i+1}v_{j+1})<w(v_iv_{i+1})+w(v_{j}v_{j+1})
w(vivj)+w(vi+1vj+1)<w(vivi+1)+w(vjvj+1) ,则以
C
i
j
C_{ij}
Cij 代替 C,称前者为改良圈。
(2)转(1),直至无法进行改进,停止。
该算法的结果几乎可以肯定不是最优的,会陷入局部最优解,但是改良圈的思想可以和智能算法结合
,进行优化。
参考:
数学建模算法与应用(第2版) 司守奎 孙兆亮
https://zhuanlan.zhihu.com/p/315210267 讨论度量空间下TSP问题的两个近似算法
https://www.jianshu.com/p/d1e004bdea8d 近似解python实现
https://www.isolves.com/it/cxkf/sf/2020-07-19/24491.html SOM(竞争神经网络)算法求解
https://zhuanlan.zhihu.com/p/467270686 TSP求解算法集合