蓝桥杯day11——堆优化版Dijkstra和Bellman-Ford算法
堆优化版Dijkstra
用于稀疏图的存储优化,使用邻接表存储图,用优先队列存储每个节点
Python中的堆
from queue import PriorityQueue as PQ
pq = PQ()
pq.put((1, 'a'))
pq.put((2, 'c'))
pq.put((2, 'b'))
pq.put((2, 'b'))
print(pq.queue) # [(1, 'a'), (2, 'b'), (2, 'b'), (2, 'c')]
item0 = pq.get() # (1, 'a')
print(pq.queue) # [(2, 'b'), (2, 'b'), (2, 'c')]
print(pq.qsize()) # 优先队列的尺寸
while not pq.empty():
print(pq.get())
例题
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
数据范围
1≤n,m≤1.5×105,
图中涉及边长均不小于 0,且不超过 10000。
数据保证:如果最短路存在,则最短路的长度不超过 109。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
from queue import PriorityQueue as PQ
N = 150010
M = 10010
h = [-1] * N
e = [-1] * N
w = [-1] * N
ne = [-1] * N
idx = 0
dist = [M] * N
st = [False] * N
def add(x, y, z) :
global idx
e[idx] = y
w[idx] = z
ne[idx] = h[x]
h[x] = idx
idx += 1
def Dijkstra() :
pq = PQ()
dist[1] = 0
pq.put([0, 1])
while not pq.empty() :
t = pq.get()
distance, ver = t[0], t[1]
if st[ver] : continue
st[ver] = True
i = h[ver]
while i != -1 :
j = e[i]
if dist[j] > dist[ver] + w[i] :
dist[j] = dist[ver] + w[i]
pq.put([dist[j], j])
i = ne[i]
if dist[n] >= M : return -1
else :
return dist[n]
n, m = map(int, input().split())
for i in range(m) :
x, y, z = map(int, input().split())
add(x, y, z)
print(Dijkstra())
小结
写题目时,在判断st和设置st那里出错过,以及
if dist[j] > dist[ver] + w[i] :
这行的理解上不熟,这里来总的说一下,dist[j]代表第j个节点到起始点的距离,dist[ver]是第ver个节点到起始点的距离,w[i]是第ver个节点到第j个节点的距离。三角关系判断。
还有出错的点就是在入队方面
Bellman-Ford算法
原理
-
松弛操作:在松弛一条边(u,v)的过程中,要测试是否可以通过u,对迄今找到的v的最短路径进行改进;如果可以改进的话,则更新d[v]和π[v]。一次松弛操作可以减小最短路径估计的值d[v],并更新v的前趋域π[v](S到v的当前最短路径中v点之前的一个点的编号)。下面的伪代码对边(u,v)进行了一步松弛操作。
-
Bellma-Ford算法,模板
for i in range(k) : for 所有边 : 松弛操作
模板讲解
首先循环k次,则对图的所有边进行k次松弛操作,意思也是从起点开始到每个节点最多经过k条边的最短距离
松弛操作就是判断到某个节点,通过某个边更短还是已有的边更短
注意点
图用边来存储
每次循环时,都要对前一次循环的距离的结果进行备份,以防串连。
例题给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。
注意:图中可能 存在负权回路 。
输入格式
第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
点的编号为 1∼n。
输出格式
输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。
如果不存在满足条件的路径,则输出 impossible。
数据范围
1≤n,k≤500,
1≤m≤10000,
1≤x,y≤n,
任意边长的绝对值不超过 10000。
输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3
import copy
Edge = []
dist = [100010] * 500
def Bellman_Ford() :
dist[1] = 0
for i in range(k) :
backup = copy.deepcopy(dist)
for e in Edge :
dist[e[1]] = min(dist[e[1]], backup[e[0]] + e[2]) #使用backup
if dist[n] >= 100010 // 2 :
return True
else :
return False
n, m, k = map(int, input().split())
for i in range(m) :
x, y, z = map(int, input().split())
Edge.append([x, y, z])
if Bellman_Ford() :
print("impossible")
else : print(dist[n])
小结
一定要多写几遍熟练熟练
总结
发现这些最短路算法的特点都是,定义一个dist数组来记录每一个起始点到节点距离,开始起始点设为0,其余距离设为无穷。下面要么对边枚举要么对点枚举
dijkstra对没有负权边有效,Bellman_Ford对于存在负权边,且对于步数有限制的情况下有效
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!