算法原理
四种算法原理请参考B站UP主麦克老师讲算法
题目——蓝桥王国:
小明是蓝桥王国的王子,今天是他登基之日。 在即将成为国王之前,老国王给他出了道题,他想要考验小明是否有能力管理国家。 题目的内容如下: 蓝桥王国一共有 NN 个建筑和 MM 条单向道路,每条道路都连接着两个建筑,每个建筑都有自己编号,分别为 1∼N1∼N 。(其中皇宫的编号为 11) 国王想让小明回答从皇宫到每个建筑的最短路径是多少,但紧张的小明此时已经无法思考,请你编写程序帮助小明回答国王的考核。 输入描述 输入第一行包含三个正整数 N,MN,M。 第 22 到 M+1M+1 行每行包含三个正整数 u,v,wu,v,w,表示 u→vu→v 之间存在一条距离为 ww 的路。 1≤N≤3×1051≤N≤3×105,1≤m≤1061≤m≤106,1≤ui,vi≤N1≤ui,vi≤N,0≤wi≤1090≤wi≤109。 输出描述 输出仅一行,共 NN 个数,分别表示从皇宫到编号为 1∼N1∼N 建筑的最短距离,两两之间用空格隔开。(如果无法到达则输出 −1−1) 3 3 1 2 1 1 3 5 2 3 2 0 1 3
Floyed算法
当问题要求任意两点之间的距离的话使用Floyed算法
n, m = map(int, input().split())
# 初始化INF的值
INF = float('inf')
# 初始化邻接矩阵
dp = [[INF for _ in range(n+1)] for _ in range(n+1)]
for _ in range(m):
u, v, w = map(int, input().split())
# 这里需要取最小,防止有两节点间有重复路径
dp[u][v] = min(dp[u][v], w)
def Floyed():
# 枚举所有结点(让其他节点试图通过该节点找到所更短路径)
for i in range(1, n+1):
dp[i][i] = 0
for u in range(1, n+1):
for v in range(1, n+1):
dp[u][v] = min(dp[u][v], dp[u][i] + dp[i][v])
Floyed()
print(dp)
for i in dp[1][1:]:
print(i, end=' ')
Dijkstra算法
当题目要求单源最短路径时,当各个边的权值都为正值的话,优先使用Dijkstra算法,Dijkstra算法的效率要优于Bellman-Ford和SPFA。
import heapq
n, m = map(int, input().split())
# 初始化INF的值
INF = float('inf')
# 初始化记录路径值的列表
dis = [INF for _ in range(n+1)]
dis[1] = 0
# 初始化记录最短路径的列表
pre = [0 for _ in range(n+1)]
# 初始化记录路径的列表
vis = [0 for _ in range(n+1)]
# 创建一个记录最小值的小根堆(模拟优先队列)
path = []
heapq.heappush(path, [0, 1])
# 创建保存路径的列表
a = [[] for _ in range(n+1)]
for _ in range(m):
u, v, w = map(int, input().split())
a[u].append([v, w])
while path:
# 取出距离最小值的结点
mine = heapq.heappop(path)[1]
if vis[mine] == 1:
continue
vis[mine] = 1
# 寻找最小距离的点的可达点
for v, w in a[mine]:
if not vis[v] and dis[v] > dis[mine]+w:
dis[v] = dis[mine] + w
pre[v] = mine
heapq.heappush(path, [dis[v], v])
def print_path(i):
if i == 0:
return
print_path(pre[i])
print(i, end='->')
for i in range(1, n+1):
print('距离点{}: '.format(i), dis[i])
print("路径为:", end=' ')
print_path(i)
Bellman-Ford算法
Bellman-Ford算法适用于解决单源最短路径中出现边的权值为负值的情况,但是当给出的路径中出现一个权值和为负值的环的情况下,该算法不能得到正确答案。另外该算法也可以用来判断路径中是否有权值和为负值的环,当双重循环结束后,理应得到所有点距目标点最近的距离,但是由于权值和为负值的环的出现,接着进行循环时有些点的最近距离会继续更新,显然这是不合理的。
n, m = map(int, input().split())
# 初始化INF的值
INF = float('inf')
# 初始化记录路径值的列表
dis = [INF for _ in range(n+1)]
dis[1] = 0
# 初始化记录最短路径的列表
pre = [0 for _ in range(n+1)]
# 初始化记录边的列表
path = [list(map(int, input().split())) for _ in range(m)]
def print_path(i):
if i == 0:
return
print_path(pre[i])
print(i, end='->')
def Bellman_Ford():
for _ in range(n-1):
for u, v, w in path:
if dis[u] + w < dis[v]:
pre[v] = u
dis[v] = w + dis[u]
# 判断是否有负权值环
for u, v, w in path:
if dis[v] > dis[u]+w:
print("存在负权值环")
raise EOFError("请确保不存在权值和为负值的环")
Bellman_Ford()
for i in range(1, n+1):
print()
print('距离点{}: '.format(i), dis[i])
print("路径为:", end=' ')
print_path(i)
print()
SPFA算法
该算法是Bellman-Ford算法改进算法,避免了很多冗余的计算,用的类似广度优先搜索的方式对最近距离进行迭代。适用场景与Bellman-Ford算法一样,同样它也能用来判断路径中是否有权值和为负值的环。
import collections
# 类似与广度优先搜索
n, m = map(int, input().split())
# 初始化INF的值
INF = float('inf')
# 初始化记录路径值的列表
dis = [INF for _ in range(n+1)]
dis[1] = 0
# 初始化记录最短路径的列表
pre = [0 for _ in range(n+1)]
# 初始化记录边的列表
path = [[] for _ in range(n+1)]
for _ in range(m):
u, v, w = map(int, input().split())
path[u].append([v, w])
# 输出路径的函数
def print_path(i):
if i == 0:
return
print_path(pre[i])
print(i, end='->')
def SPEA():
a = collections.deque([])
a.appendleft(1)
while a:
p = a.popleft()
for v, w in path[p]:
if dis[p] + w < dis[v]:
dis[v] = dis[p] + w
pre[v] = p
a.append(v)
SPEA()
for i in range(1, n+1):
print()
print('距离点{}: '.format(i), dis[i])
print("路径为:", end=' ')
print_path(i)
print()