1. 问题描述
Dijkstra算法解决的是带权重的有向图上单源最短路径问题。
所有边的权重都为非负值。
设置顶点集合S并不断地作贪心选择来扩充这个集合。使用最小堆数据结构构造优先队列。
2. 输入形式
在屏幕上输入顶点个数和连接顶点间的边的权矩阵。
3. 输出形式
从源到各个顶点的最短距离及路径。
4. 样例
4.1 样例输入
5
0 10 0 30 100
0 0 50 0 0
0 0 0 0 10
0 0 20 0 60
0 0 0 0 0
4.2 样例输出
10: 1->2
50: 1->4->3
30: 1->4
60: 1->4->3->5
4.3 样例说明
输入:顶点个数为5;
连接顶点间边的权矩阵大小为5行5列;
位置[i,j]上元素值表示第i个顶点到第j个顶点的距离,0表示两个顶点间没有边连接。
输出:每行表示源1到其余各顶点的最短距离及路径。
5. 问题分析
输入包含了一个有权重的有向图G,然后求从顶点1开始到其他顶点的最短距离;
由输入格式,可得该题有向图如下图所示:
6. 算法的基本思想
Dijkstra算法是解决单源最短路径的一个贪心算法。
其基本思想是:
每次新扩展一个距离最短的点,更新与其相邻的点的距离。
当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。
不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离不会改变的性质。
7. 代码
import numpy as np
def Result(prev, t):
if prev[t] != 0:
Result(prev, prev[t])
print('->{}'.format(prev[t] + 1), end="")
def Dijkstra(n, weights):
# 创建一个flag数组,用于保存遍历情况
flag = np.zeros(n, bool)
# 创建一个dist数组,用于保存最短路径
dist = np.array(weights[0])
# 创建一个prev数组,用于保存对应的最短节点
prev = np.zeros(n, int)
# 将源节点放入集合S中
flag[0] = True
# Dijkstra算法中重点:
# ~~错误思路:迭代(n+1)/2次,因为每次可以确定两个节点
# 迭代次数为n-1次,因为如果确定某一节点,但其最小值不会影响其他节点,每次迭代只能确定一个节点;
# 依次将节点放入集合S中(即已访问过的节点);
for i in range(n-1):
# 找到当前dist中还未被遍历的节点中权值最小的节点;
# 并将其放入集合S中;
temp = float('inf')
u = 0
for j in range(n):
if not flag[j] and dist[j] != 0 and dist[j] < temp:
u = j
temp = dist[j]
flag[u] = True
# 确定当前节点最短距离后,其他节点最短距离是否随之改变,若改变,即可确定其最短路径;
for j in range(n):
if not flag[j] and weights[u][j] != 0:
if dist[u] + weights[u][j] < dist[j] or dist[j] == 0:
dist[j] = dist[u] + weights[u][j]
prev[j] = u
# 输出结果
for i in range(1, n):
print('{}:1'.format(dist[i]), end="")
# 递归函数,因为prev中是从后往前读取节点
Result(prev, i)
print("->{}".format(i + 1))
def main():
n = int(input())
weight = []
for i in range(n):
weight.append(list(map(int, input().rstrip().split())))
weights = np.array(weight).reshape(n, n)
Dijkstra(n, weights)
if __name__ == '__main__':
main()
*注:CG平台只支持ASCII,不支持UTF-8,所以在输入->
这玩意,还是直接复制上面输出示例中的->