堆优化版Dijkstra和Bellman-Ford算法

堆优化版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算法

原理

  1. 松弛操作:在松弛一条边(u,v)的过程中,要测试是否可以通过u,对迄今找到的v的最短路径进行改进;如果可以改进的话,则更新d[v]和π[v]。一次松弛操作可以减小最短路径估计的值d[v],并更新v的前趋域π[v](S到v的当前最短路径中v点之前的一个点的编号)。下面的伪代码对边(u,v)进行了一步松弛操作。

  2. 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对于存在负权边,且对于步数有限制的情况下有效

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值