文章目录
前言
随着对图论的深入,内心产生了很多问题。
之前的文章:图论最短路及生成树(Prim,Djikstra,Spfa,Bellan-ford,kruskal,topsort)
一、对Bellman-Ford的深入理解
1. Bellman-Ford有什么用?
Bellman-Ford算法效率低于dijistra算法,但具有一定实际意义。可以解决有边数限制的最短路问题。
想象这样一个场景:你在一号城市,想去n号城市去旅游。但是飞机没有1号城市从直达n号城市的(只能换乘飞机到达)。你想要花费最少到达目的地;但是吧,每换乘一次,你的心情就会变差一回,也就是说最多换乘k次。问最优解是什么?
Bellman-ford算法:
可以用于判断是否存在负权回路
步骤:
for k次循环(最多经过k条边)
for 所有边a,b,w(权重)
更新:dist[b] = min(dist[b],dist[a]+w) // 即松弛操作
负权环,指的是一个图中存在一个环,里面包含的边的边权总和<0
2. 什么是松弛操作?
松弛操作:为一次成功的更新dist[x]操作
每一次成功的松弛操作,都意味着我们发现了一条新的最短路。
3. Bellman-Ford的k次迭代意义?
- Bellman-Ford:共迭代k次,每次迭代,对图中所有的点的dist进行一次松弛操作,即更新一次最短路
- k次迭代的实际意义:每次迭代k,我们找到了经历了k条边的最短路。
4. 一个重要定理
只有上一次迭代中松弛过的点才有可能参与下一次迭代的松弛操作。
5. 对于模板的深入理解
- 大致过程:
设置一个起点令dist[起点] = 0,此时认为起点进行过一次松弛操作。每次通过上次进行过松弛操作的点去更新可能参与下一次松弛操作的点,这有点像BFS,一圈一圈计算,有边数限制的话则最多松弛k次。
int Bellman_ford(){
memset(dist,0x3f,sizeof(dist));
dist[1] = 0;
for(int i=0;i<k;i++){
memcpy(backup,dist,sizeof dist);
for(int i=0;i<m;i++){
int a = edges[i].a,b = edges[i].b,c = edges[i].w;
dist[b] = min(dist[b],backup[a]+c);
}
}
if(dist[n] > 0x3f3f3f3f/2) return -1;
else return dist[n];
}
- k次迭代的意义? 见上
- 为什么要用backup数组?
backup保存的前几次发生松弛操作的点,依据定理:这些发生过松弛的点可能参与下一次的松弛操作进而更新更多的点。每次进行一大次松弛操作的时候,不能动态更新,所以需要backup数组。因为松弛操作只能更新一圈的点,这样容易更新紊乱。
二、Bellman-Ford到SPFA
将Bellman-Ford中遍历每条边的做法改变为遍历那些被成功松弛的点的邻点,我们可以简单地通过一个队列来维护这些被成功松弛的点,这个小小的改进可以带来巨大加速,改进之后的算法被称为SPFA。
SPFA算法模板:见之前博客(上面链接)。
解决:1. 最短路。 2. 判断负环。
比较:SPFA与Dijkstra
三、 一点问题
为什么采用结点入队次数来判断负环比统计当前每个点的最短路中所包含的边数是否大于n的运行时间多?
文章部分参考:深入理解Bellman-Ford(SPFA)算法