旅行商问题(TSP)是著名的NP问题,最暴力的做法是枚举点的排列,因此时间复杂度是O(n!).
一种较好的精确算法是动态规划,时间复杂度是O(n22n).
最近遇到了使用动态规划求25个点的TSP问题,浮点数输入,精确解输出.
这个数据在时间和空间上都很糟糕,尤其是空间,原始的算法占用内存过大.
于是和一些同学进行了讨论,把得到的想法思路在此整理一下.
基本框架
用动态规划做TSP,就是在一个有向无环图(DAG)上做dp,图上的每个节点是一个二元组的状态(i,S),对应一条路径.
其中i表示当前路径的最后一个点,S是一个集合,表示路径上的点集.
那么,每个状态(i,S)的所有前驱(j,S'),一定会满足:
j∈S且S=S'+{i}
递推方程为:
dp(i,S)=min(dp(j,S-{i})+cost(i,j)) , j∈S且j≠i
方程的简单理解就是,到j的最短距离,加上j到i的最短距离,就是对应的i的最短距离.
集合S在具体实现上要映射到一个数字,我们可以用二进制串的状态压缩.
一个二进制串的第i位就表示第i个点是否在S里,为1表示在,为0表示不在.
每个状态需要枚举j,因此时间上是O(n).状态一共有O(n2n)个(包含一些非法状态),因此总的时间复杂度是O(n22n).
而空间复杂度,最占空间的dp数组需要O(n2n)的空间,因此空间复杂度是O(n2n).
回路处理
由于TSP要求的是一个最短的回路,即起点终点为同一个点.
由于是回路,因此设哪个点是起点其实没有所谓,简单起见,不妨设第n-1个点为起点(下标从0开始).
这样处理后,在dp过程中,可以完全不考虑第n-1个点,然后适当处理一下dp的开头和结尾.
dp的初始化为:
dp(i,{i})=cost(i,n-1) , i∈[0,n-1)
dp最后的答案为:
min(dp(i,Sfull)+cost(i,n-1)) , i∈[0,n-1) , Sfull={0,1,2,...n-2}
这样处理dp(i,S)的含义就是从n-1为起点,经过了S里面的点,并且最后到达i的最短距离.
Sfull这个状态就表示所有的点都经过了一遍,就差回