**【问题描述】**采用深度优先搜索算法求解TSP问题,并在搜索过程中,使用界限条件(当前结点已经走过的路径长度要小于已求得的最短路径)进行“剪枝”操作(不再对后续结点进行遍历),从而提高搜索效率。采用queue模块中的栈(LifoQueue)来实现深度优先搜索。
**【输入形式】**在屏幕上输入顶点个数和连接顶点间的边的邻接矩阵。
**【输出形式】**最优值和其中一条最优路径。
【样例输入】
4
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0
【样例输出】
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 4]
[1, 3]
[1, 3, 2]
[1, 3, 2, 4]
[1, 3, 4]
[1, 4]
[1, 4, 2]
[1, 4, 2, 3]
[1, 4, 3]
25: [1, 3, 2, 4]
【样例说明】
输入:顶点个数为4。连接顶点间边的邻接矩阵大小为4行4列,位置[i,j]上元素值表示第i个顶点到第j个顶点的距离,0表示两个顶点间没有边连接。
输出:最优值为25,最优路径为[1, 3, 2, 4]。
**【评分标准】**根据输入得到准确的输出。
python实现:
import math # 回溯法求解旅行商问题
'''
path中最开始只有源点1,对顶点按照从小到大的全排列顺序一个一个判断是否到达叶子节点时,再回到源点有最短路径
深度优先搜索,模拟解答树过程
'''
bestcost = math.inf # 最短路径
nowcost = 0 # 当前的路径长度
x = [1, 2, 3, 4, 5, 6] # 顶点序号
path = [1] # 模拟open表中过程
arc = [] # 存放
def main():
n = int(input()) # 顶点个数
graph = [] # 图的邻接矩阵
for i in range(n):
graph.append(list(map(int, input().split())))
tsp(graph, 1, n)
print(bestcost, end=": ")
print(arc[:n])
def tsp(graph, s, n): # 深度优先搜索,模拟解答树过程
global nowcost, bestcost, arc
print(path)
if s == n:
if graph[x[n - 1]-1][x[0]-1] != 0 and (nowcost + graph[x[n - 1]-1][x[0]-1] < bestcost):
arc = x[:]
# print("arc", arc)
bestcost = nowcost + graph[x[n - 1]-1][x[0]-1]
# print('bestcost', bestcost)
else:
for i in range(s, n):
# 求得的值大于目前的最佳值的放弃搜索,“剪枝”
if graph[x[i - 1]-1][x[i]-1] != 0 and nowcost + graph[x[i - 1]-1][x[i]-1] < bestcost:
x.insert(s, x[i]) # 从小到大生成全排列的过程
del x[i+1]
# x.remove(x[i+1]) # 这样删除结果有错误,为啥
# x[i], x[s] = x[s], x[i] # 全排列
# print(x)
nowcost += graph[x[s - 1]-1][x[s]-1] # 将花费加入
path.append(x[s])
tsp(graph, s+1, n)
nowcost -= graph[x[s - 1]-1][x[s]-1] # 回溯上去还需要减去
path.remove(x[s])
# x[i], x[s] = x[s], x[i] # 这样的全排列不是从小到大的
x.insert(i+1, x[s])
del x[s]
# x.remove(x[s]) # 这样删除结果错误,为啥
if __name__ == '__main__':
main()
'''
样例输入
4
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0
'''
列表删除那里的错误原因:
remove(value)
del(index)
也就是说两个删除函数的参数意义不同,注意区分。
老师的参考代码:
import numpy as np
import queue as q
class VertexNode(object): # 顶点类
def __init__(self, path=None, cost=None):
self.path = path # 到当前结点为止已经走过的路径
self.cost = cost # 到当前结点为止的费用
def tst_dfs(w_array, s_index):
# 初始化最优值和最优路径
best_cost = np.inf
best_path = None
vex_num, _ = w_array.shape
# 初始化起点结点和栈,将起始节点加入栈
start_node = VertexNode(path=[s_index], cost=0)
open_queue = q.LifoQueue()
open_queue.put(start_node)
# 当栈中非空时,循环
while not open_queue.empty():
cur_node = open_queue.get()
print(cur_node.path)
# 如果当前结点的费用大于最优值,则无需进行扩展或者判断
if cur_node.cost >= best_cost:
continue
# 如果当前结点已经为叶结点,则判断当前结点费用加上回到起始点的 # 费用和是否小于最优值,若是则替换
# 判断结束后,跳过后续程序,直接执行下一循环
if len(cur_node.path) == vex_num:
new_cost = cur_node.cost + w_array[cur_node.path[-1]-1, s_index-1]
if new_cost < best_cost:
best_cost = new_cost
best_path = list(cur_node.path)
continue
# 当前结点非叶子节点,则扩展当前结点,将加新节点到栈中
for i in range(vex_num, 0, -1):
if not(i in cur_node.path):
new_cost = cur_node.cost + w_array[cur_node.path[-1]-1, i-1]
# 当新节点的费用小于最优值时,才将新结点加入到栈中,否则执行剪枝操作
if new_cost < best_cost:
new_path = list(cur_node.path)
new_path.append(i)
new_node = VertexNode(path=new_path, cost=new_cost)
open_queue.put(new_node)
return best_cost, best_path
def main():
vex_num = int(input()) # 顶点个数
w_array = np.zeros((vex_num, vex_num), dtype=np.double) # 邻接矩阵
for i in range(vex_num):
w_array[i, :] = np.array(input().split(), dtype=np.double)
w_array[w_array == 0] = np.inf
s_index = 1 # 起始点序号
best_cost, best_path = tst_dfs(w_array, s_index)
print('{}: {}'.format(int(best_cost), best_path))
if __name__ == '__main__':
main()