“旅游规划”的题面如下:
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。
现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。
如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);
M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。
随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。
输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
这类给出点之间的关系并给出确定的起点,要求最短路径的问题,适用 Dijkstra 算法求解。
一般点使用序号表示,给出边长度 (这里是路程和花费) 的场合,可以使用邻接矩阵存储图的信息。
Dijkstra 算法保证访问包含起点在内且与起点连通的所有图中的点。每次访问一个点时,寻找当下所有未访问点中离起点最近的那一个,然后判断“如果通过现在访问的点走向那个最近点,会不会比原来的路径更短” 。完全访问所有与起点连通的点后,算法结束。
Dijkstra 算法的时间复杂度主要取决于“寻找当下未访问点中离起点最近的那一个”的方式。
Dijkstra 算法无法处理含负值边的图,存在负值边时会在图中形成一个负值圈,无法找到最短路 (因为加上一个负值总是会让被加的数更小)。
以下是使用 python 解题的代码。受限于算法表示和语言性能,测试点 "最大N和M, 随机完全图" 运行超时,如果使用 C/C++ 表示相同的逻辑则可以通过该测试点。
# 邻接矩阵描述图,有插入边的方法
class Graph:
INFINITY = 65535
# 初始化描述为一个值全为无穷大的空图
def __init__(self, vertex_num):
self.vertex_num = vertex_num
self.graph_far = []
self.graph_cost = []
for i in range(vertex_num):
self.graph_far.append([self.INFINITY]*vertex_num)
self.graph_cost.append([self.INFINITY]*vertex_num)
# 向无向图插入边传入的边信息包含在列表内
def insert_edge(self, edge):
start, end, long, cost = edge[0], edge[1], edge[2], edge[3]
self.graph_far[start][end] = long
self.graph_far[end][start] = long
self.graph_cost[start][end] = cost
self.graph_cost[end][start] = cost
class Dist:
def __init__(self, it_len, full_val=0):
self.body = []
for i in range(it_len):
self.body.append(full_val)
# 找一个在 dist 中存放路程最短的点,找不到则返回 INFINITY
def find_min_dist(graph, dist, collected):
min_vertex = graph.INFINITY
min_dist = graph.INFINITY
for ver in range(graph.vertex_num):
if not collected[ver] and dist.body[ver] < min_dist:
min_dist = dist.body[ver]
min_vertex = ver
return min_vertex
# dist 和 cost 分别描述路程和花费
def dijkstra(graph, dist, cost, path, start_city):