算法-图论-最短路径
0. 不同的最短路径算法
1. dijkstra 朴素(卡码网 47)
题目条件:
- 不存在负权值的边
图的存储:邻接矩阵
dijkstra的基本思路:
- 维护
minDist[]
,表示各顶点到起点的最短距离;维护visited[]
,访问过的顶点不会重复访问。 - 对未访问过的
visited[j] == False
且minDist[j]
最小的顶点 进行访问 - 更新 j 加入后的minDist[]
# 起点为start,终点为end
# dijkstra能求单源到所有点的最短路
def dijkstra(num_node, grid, start, end):
# 各点到起点1的距离
minDist = [min(grid[start][i], float('inf')) for i in range(num_node+1)]
visited = [False for _ in range(num_node+1)]
# 初始化
minDist[start] = 0
visited[start] = True
# 访问剩下的n-1个点
for i in range(num_node-1):
minVal = float('inf')
cur = start
for j in range(1, num_node+1):
if visited[j] == False and minDist[j] < minVal:
minVal = minDist[j]
cur = j
visited[cur] = True
# 更新minDist[]
for j in range(1, num_node+1):
if visited[j] == False and minDist[cur] + grid[cur][j] < minDist[j]:
minDist[j] = minDist[cur] + grid[cur][j]
if minDist[end] == float('inf'):
return -1
else :
return minDist[end]
def main():
num_node, num_edge = map(int, input().split())
grid = [[float('inf') for _ in range(num_node+1)] for _ in range(num_node+1)]
for _ in range(num_edge):
source, target, val = map(int, input().split())
grid[source][target] = val
result = dijkstra(num_node, grid, 1, num_node)
print(result)
if __name__ == "__main__":
main()
2. dijkstra 堆优化版 (卡码网 47)
图的存储:邻接表
import heapq
class Edge:
def __init__(self, target, val):
self.target = target
self.val = val
# 堆优化版的dijkstra,适合稀疏图
def dijkstra(num_node, num_edge, grid):
visited = [False for _ in range(num_node+1)]
minDist = [float('inf') for _ in range(num_node+1)]
# 使用小根堆维护的优先队列
pq = []
# 将item=(0,1)插入队列
heapq.heappush(pq, (0, 1))
minDist[1] = 0
while pq:
cur_dist, cur_node = heapq.heappop(pq)
if visited[cur_node]:
continue
visited[cur_node] = True
for edge in grid[cur_node]:
if not visited[edge.target] and cur_dist + edge.val < minDist[edge.target]:
minDist[edge.target] = cur_dist + edge.val
heapq.heappush(pq, (minDist[edge.target], edge.target))
if minDist[num_node] == float('inf'):
return -1
else :
return minDist[num_node]
def main():
num_node, num_edge = map(int, input().split())
# 邻接表存储
grid= [[] for _ in range(num_node+1)]
for _ in range(num_edge):
s, t, val = map(int, input().split())
grid[s].append(Edge(t, val))
result = dijkstra(num_node, num_edge, grid)
print(result)
if __name__ == "__main__":
main()
3. Bellman_ford 算法(卡码网 94)
题目条件:
- 存在负权值的边
- 不存在负权回路
要求:求起点1到终点n的最短路径(可以为负值)。
图的存储:邻接表
算法思路:
- 因为不存在负回路,所以起点1到任何顶点的最短路径上的边数都小于等于n-1
- 所以
松弛n-1次
必定收敛 - 松弛就是更新
minDist[]
,每次松弛遍历一次所有的边。 - 时间复杂度为\(O(N*E)\)
if minDist[e.t] < minDist[e.s] + e.val:
minDist[e.t] = minDist[e.s] + e.val
class Edge:
def __init__(self, s, t, val):
self.s = s
self.t = t
self.val = val
# 有负权边的单源最短路
def bellman_ford(num_node, num_edge, edges):
# 各点到起点1的最短距离
minDist = [float('inf') for _ in range(num_node+1)]
minDist[1] = 0
# 松弛 n-1 次,第i次循环得到的是与起点距离i条边的顶点的minDist
for _ in range(num_node-1):
updated = False
for e in edges:
if minDist[e.s] != float('inf') and minDist[e.s] + e.val < minDist[e.t]:
minDist[e.t] = minDist[e.s] + e.val
updated = True
# 如果提前收敛,则跳出循环
if updated == False:
break
if minDist[num_node] == float('inf'):
print("unconnected")
else :
print(minDist[num_node])
def main():
num_node, num_edge = map(int, input().split())
edges = []
for _ in range(num_edge):
s, t, v = map(int, input().split())
edges.append(Edge(s, t, v))
bellman_ford(num_node, num_edge, edges)
if __name__ == "__main__":
main()
4. 使用Bellman_Ford算法判断负回路
思路:
- 松弛次数为
n次
。若不存在负回路,则n-1次就收敛了。若第n次仍然发生了更新,说明存在负回路。
python
- 使用本代码中的读取输入的方式比原先的读取方式更快。
import sys
def bellman_ford(num_node, num_edge, edges):
# 城市1到各节点的最短距离
minDist = [float('inf')] * (num_node+1)
minDist[1] = 0
flag = False
# 松弛n次
for i in range(1, num_node+1):
if i < num_node:
for e in edges:
start = e[0]
to = e[1]
val = e[2]
if minDist[start] != float('inf') and minDist[start] + val < minDist[to]:
minDist[to] = minDist[start] + val
else :
# 第n次回路权值仍变小,说明存在负回路
for e in edges:
start = e[0]
to = e[1]
val = e[2]
if minDist[start] != float('inf') and minDist[start] + val < minDist[to]:
minDist[to] = minDist[start] + val
flag = True
if flag :
print("circle")
elif minDist[num_node] == float('inf'):
print("unconnected")
else :
print(minDist[num_node])
def main():
# 这种方式的读取输入更快
input = sys.stdin.read
data = input().split()
index = 0
num_node = int(data[index])
index += 1
num_edge = int(data[index])
index += 1
edges = []
for i in range(num_edge):
s = int(data[index])
index += 1
t = int(data[index])
index += 1
val = int(data[index])
index += 1
edges.append([s, t, val])
bellman_ford(num_node, num_edge, edges)
if __name__ == "__main__":
main()