项目里突然出现了一个类似TSP旅行商的问题,稍微有点儿变动的可能是需要指定终点,而不是回到起点。
因为项目里设计规划的点的比较少(<20)又要求比较准确,所以采用了动态规划法。
动态规划算法的定义就不多做介绍了, 下面直接来到解决思路。
假设有N个城市,dp[N][N]这个二维数组保存了 各个城市之间的距离
那么问题就可以简化为 从0(p)点出发到还未走过的城市集合 S(N-1)的最短距离
定义为 min_distance(S, 0) ,
假设第二个到达城市j,那么剩下的最短距离min_distance(S-j,j) (S-j 表示S集合去掉j点,先这么表示,后面具体再说)
就有min_distance(S, 0) = min_distance(S-j, j) + dp[j][0],这也就是我们的递归公式
如果我们要取最小值的话 上面的j城市就要遍历从 1 到 (N-1)的点 然后取最小,我们用min{}表示取{}集合里的最小值
也就是 min { min_distance(S-j, j) + dp[j][0] }
接下来我们用代码展示一下(以Python为例,后面会贴上iOS的demo)
我们用整形来表示集合S,一个整形是32位,因为S集合是没有去过的点,我们用1来表示没有去过,0表示已经去过
首先我们对S 初始化
s = 0
for i in range(1,N-1): # 从1开始
s = s|(1 << i)
# 遍历完之后 就表示除了起点之外的所有集合
接下来定义下min_distance函数
# 这里有个关键问题 就是当已经到i城市的话 如何从S集合里去除i ,因为我们使用位表示的s集合,所以需要s & 该位的取反 也就是 ~(1< # 最终表示为 s & (~(1<
也就是min_distance(s, 0) = min { min_distance(s & (~(1<
当0 为 任意一个城市的时候就有了
def min_distance(s, p):
min_length = 9999999 # 记录 最小距离
for i in range(1, N): # 从1点开始遍历
if s & (1 << i): #如果i在S集合里,也就是i是没有去过的城市
ret = min_distance(s & (~(1<
当这段代码写出来的时候 大家也大概看出了 差不多就是穷举法,就是要把所有的距离都算出来,取最小值。
当然这还不算完,因为有大量的重复计算,举个例子
有 0 1 2 3 4 5个城市
假设已走过 0 1 2 3三个城市 现在按顺序0 - 1 -2 - 3走,求3 到S{4, 5}的最小距离。
然后我们不按顺序走 0 - 2- 1 -3 ,现在也是 求3 到S{4, 5}的最小距离,这时我们发现min_distance(s{4,5} , 3)求了两遍。
这只是举个例子,这种递归里肯定有大量的重复运算。
所以我们用一个二维数组记录已经算过的距离 visited[2^N][N]初始化所有的值为-1,然后记录所有的最小距离(2^N 与 s集合对应)
当求值的时候首先判断一下 该值是否为-1 若不是 直接返回,于是改进后
def min_distance(s, p):
if visited[s][p] != -1: #若之前计算过 ,直接返回
return visited[s][p]
min_length = 9999999 # 记录 最小距离
for i in range(1, N): # 从1点开始遍历
if s & (1 << i): #如果i在S集合里,也就是i是没有去过的城市
ret = min_distance(s & (~(1<
我们还有一个需求 就是要求算出的结果要回到第N个城市 在代码里也就是第N-1个城市
也就是当S集合里只剩一个城市N-1的时候也要退出,返回值为该点到N-1城市的距离 也就是dp[N-1][p]
同时我们的遍历条件 之前是 for i in range(1,N),现在第N-1个点也不需要遍历
def min_distance(s, p):
if visited[s][p] != -1: #若之前计算过 ,直接返回
return visited[s][p]
if s == ( 1<< (N-1)): # 当是最后一个城市N的时候,直接返回该点到城市N的距离
return dp[N-1][p]
min_length = 9999999 # 记录 最小距离
for i in range(1, N-1): # 从1点开始遍历,不包括最后一个点N-1
if s & (1 << i): #如果i在S集合里,也就是i是没有去过的城市
ret = min_distance(s & (~(1<
还记得如何调用这个方法吗
s = 0
for i in range(1,N-1): # 从1开始
s = s|(1 << i)
# 遍历完之后 就表示除了起点之外的所有集合
distance = min_distance(s,0)# 从0 点 - 到所有没去过的城市结合
print(distance)
到这里的话 我们只能计算出从起点到N城市的最短距离,却没有记录路径 (从0 怎么到 N的?)
那怎么记录呢?大家如果理解了以上的话,应该不是难题。
Demo来了哦