基础
刷题
- 城市间货物运输 I
Bellman_ford 队列优化算法 (又称SPFA)
Bellman_ford 算法 是将每一个边 执行n-1次,中间会有大量的不需要的尝试,例如
- 判断边的起始节点是否为 math.MaxInt32
- 有的时候并不需要n-1次,n-1次只是最大值
故需要一个队列,先从原点开始进入队列,取出节点,将所有以该节点的边遍历(使用邻接表存储),如果小于到达的节点的misDist,就更新misDist, 并将到达的节点加入到队列(这里可以判断该节点不在队列的情况下加入队列)
- 城市间货物运输 II
对于Bellman_ford 算法,将每一个边松弛n-1次,可以再执行第n次,查看minDist数组是否有变化,如果有变化,则可以判断是存在负回路。
因为n-1是最长的遍历次数,实际中从1到n的城市,经历城市最多的路径,就是每一个城市都经过,边的数量为n-1
对于Bellman_ford 队列优化算法,代码随想录中的讲解有点抽象,他认为一个节点的入度最大为n-1,就是满图,每一个城市都一个边指向这个节点,需要计算该节点进入队列的次数为count[i], count[i] > n-1, 就说明出现了负权回路。
实际上,看着这个count[i] 和 入度没太大关系。Bellman_ford 对列优化算法,使用队列对需要经过的节点进行排序,可以理解为是广度优先算法,结合前面讲的边的数量最大是n-1,每一个节点最多被每一次的层序遍历执行一次(这是队列优化算法中 优化节点加入队列之前,判断队列是否存在这个节点),这样的次数就是 他说的 count[i],这个次数需要小于n,大于n-1,就说明出现了负权回路
举个例子:
1到4的路径:
1,4 1,2,4 1,3,4 1,3,2,4
一共有4个路径可以到达4,但是count[4] 就是 4吗?你需要去重,因为1,2,4 和 1,3,4 都是节点数为3的路径,在层序遍历中,会发现4已经在队列中不会加入队列。这个是必须要注意的。
- 城市间货物运输 III
解法一:
使用Bellman_ford 算法,使用朴素存储,将每个边松弛k次。重点是使用了copy_minDist, 每一次全量松弛,只松弛出发点(minDist[i] != math.MaxInt32)后的一条边。
解法二:
Bellman_ford 队列优化算法,但不是最优解法,在之前的题目中,不限制k的大小,故图基本上都是稀疏图,会显著提升效率,但是本题中k的值越小,Bellman_ford 算法的遍历次数越小,但是对列优化算法依然需要定义队列,各种数组切片,内存消耗以及数组的变化不会随k的变化而降低
如图,提交历史中第一个是Bellman_ford 队列优化算法,第二个是Bellman_ford算法,在稠密图中,Bellman_ford队列优化算法不占优势。
是否可以用dijkstra算法?
答案是不行的,dijkstra算法主要是使用贪心的思路,寻找距离原点最近的节点,本质和本题中需要的通过节点个数k没有任何关系。
总结
这几道题 bellman_ford 很值得再学习,只是学到一些皮毛。