最短路径之Bellman-Ford算法----解决负权边
2017-04-22 18:46
148 查看
最短路径之Bellman-Ford算法----解决负权边
//核心代码,只有4行,可以解决带有负权边的图。
dis数组作用与Dijkstra算法一样,用来记录源点到其余各个顶点的最短路径 最短路径之Dijkstra算法 .
u、v、w三个数组是用来记录边的信息。
上述代码的意思:看看能否通过u[i]到v[i]这条边(权值为w[i]),使得1号顶点到v[i]号顶点的距离变短。即1号顶点到u[i]号顶点的距离dis[u[i]]加上 u[i]到v[i]这条边[b](权值为w[i])的值是否会比原先1号顶点到v[i]号顶点的距离(dis[v[i]])要小。 [/b]
第一轮在对所有的边进行松弛之后,得到的是从1号顶点“只能经过一条边”到达其余各顶点的最短路径长度。第二轮在对所有的边进行松弛之后,得到的是从1号顶点“最多经过两条边”到达其余各顶点的最短路径长度。如果进行K轮的话,得到的是1号顶点“最多进行k条边”到达其余各顶点的最短路径长度。需要进行多少轮?
需要进行n-1轮就可以。因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1条边。
最短路径中是否包含回路?
不可能。最短路径肯定是一个不包含回路的简单路径。回路分为正权回路(即回路权值之和为正)和负权回路(即回路权值之和为负)。如果最短路径中包含正权回路,那么去掉这个回路,一定可以得到更短的路径。如果最短路径中包含负权回路,那么肯定没有最短路径,因为每多走一次负权回路就可以得到更短的路径。因此,最短路径肯定是一个不包含回路的简单路径,即最多包含n-1条边,所以进行n-1轮松弛就可以。
Bellman-Ford算法完整代码
测试数据:
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
运行结果:0 -3 -1 2 4
总结:因为最短路径上最多有n-1条边,因此Bellman-Ford算法最多有n-1个阶段。在每一个阶段,我们对每一条边都要执行松弛操作。其实每实施一次松弛操作,就会有一些顶点已经求得其最短路,即这些顶点的最短路的“估计值”变为“确定值”。此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响(但是每次还是会判断是否需要松弛,这里浪费了时间,还可以优化)。在前k个阶段结束后,就已经找出了从源点发出“最多经过k条边”到达各个顶点的最短路。直到进行完n-1个阶段后,便得出了最多经过n-1条边最短路
。
############################################################################
Bellman-Ford算法可以检测一个图是否含有负权回路。如果在进行n-1次松弛后,仍然存在
Bellman-Ford算法时间复杂度是O(NM),比dijkstra复杂度还要高,还可以进行优化。实际操作中,该算法经常会在未达到n-1轮松弛前就计算出最短路,n-1其实是最大值。因此可以添加一个变量check来标记dis数组在本轮松弛中是否发生变化,如果没有发生变化,则可以提前跳出循环。代码如下:
Bellman-Ford算法的优化:在每一次松弛操作后,就会有一些顶点已经求得其最短路,此后这些顶点的最短路的估计值就会一直保持不变,不再受后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。这就启发:每次仅对最短路径估计值发生变化了的顶点的所有出边执行松弛操作。
//核心代码,只有4行,可以解决带有负权边的图。
for(k=1;k<=n-1;k++) //外循环循环了n-1次(n为顶点个数) ,为什么只需要进行n-1轮就可以???因为在一个含有n个顶点的图中,任意两点之间的最短路径 //最多包含n-1条边 for(i=1;i<=m;i++) //内循环循环了m次(m为边的个数),即枚举每一条边 if(dis[v[i]] > dis[u[i]+w[i]]) dis[v[i]] = dis[u[i]+w[i]];
dis数组作用与Dijkstra算法一样,用来记录源点到其余各个顶点的最短路径 最短路径之Dijkstra算法 .
u、v、w三个数组是用来记录边的信息。
for(i=1;i<=m;i++) //内循环循环了m次(m为边的个数),即枚举每一条边 if(dis[v[i]] > dis[u[i]+w[i]]) dis[v[i]] = dis[u[i]+w[i]];
上述代码的意思:看看能否通过u[i]到v[i]这条边(权值为w[i]),使得1号顶点到v[i]号顶点的距离变短。即1号顶点到u[i]号顶点的距离dis[u[i]]加上 u[i]到v[i]这条边[b](权值为w[i])的值是否会比原先1号顶点到v[i]号顶点的距离(dis[v[i]])要小。 [/b]
第一轮在对所有的边进行松弛之后,得到的是从1号顶点“只能经过一条边”到达其余各顶点的最短路径长度。第二轮在对所有的边进行松弛之后,得到的是从1号顶点“最多经过两条边”到达其余各顶点的最短路径长度。如果进行K轮的话,得到的是1号顶点“最多进行k条边”到达其余各顶点的最短路径长度。需要进行多少轮?
需要进行n-1轮就可以。因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1条边。
最短路径中是否包含回路?
不可能。最短路径肯定是一个不包含回路的简单路径。回路分为正权回路(即回路权值之和为正)和负权回路(即回路权值之和为负)。如果最短路径中包含正权回路,那么去掉这个回路,一定可以得到更短的路径。如果最短路径中包含负权回路,那么肯定没有最短路径,因为每多走一次负权回路就可以得到更短的路径。因此,最短路径肯定是一个不包含回路的简单路径,即最多包含n-1条边,所以进行n-1轮松弛就可以。
Bellman-Ford算法完整代码
#include<stdio.h> int main() { int d[10],i,k,n,m,u[10],v[10].w[10]; int inf=99999999; scanf("%d %d",n,m); for(i=1;i<=m;i++) scanf("%d %d %d",&u[i],&v[i],&w[i]); //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程 for(i=1;i<=n;i++) dis[i]=inf; dis[1]=0; //核心代码 for(k=1;k<=n-1;k++) for(i=1;i<=m;i++) if(dis[v[i]] > dis[u[i]+w[i]]) dis[v[i]] = dis[u[i]+w[i]]; for(i=1;i<=n;i++) printf("%d ",dis[i]); getchar();getchar(); return 0; }
测试数据:
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
运行结果:0 -3 -1 2 4
总结:因为最短路径上最多有n-1条边,因此Bellman-Ford算法最多有n-1个阶段。在每一个阶段,我们对每一条边都要执行松弛操作。其实每实施一次松弛操作,就会有一些顶点已经求得其最短路,即这些顶点的最短路的“估计值”变为“确定值”。此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响(但是每次还是会判断是否需要松弛,这里浪费了时间,还可以优化)。在前k个阶段结束后,就已经找出了从源点发出“最多经过k条边”到达各个顶点的最短路。直到进行完n-1个阶段后,便得出了最多经过n-1条边最短路
。
############################################################################
Bellman-Ford算法可以检测一个图是否含有负权回路。如果在进行n-1次松弛后,仍然存在
if(dis[v[i]] > dis[u[i]+w[i]]) dis[v[i]] = dis[u[i]+w[i]];说明进行n-1次松弛后,仍然可以继续成功松弛,此图必然存在负权回路。关键代码如下:
for(k=1;k<=n-1;k++) for(i=1;i<=m;i++) if(dis[v[i]] > dis[u[i]+w[i]]) dis[v[i]] = dis[u[i]+w[i]]; //检测负权回路 flag=0; for(i=1;i<=m;i++) if(dis[v[i]] > dis[u[i]+w[i]]) flag=1; if(flag==1) printf("此图必然含有负权回路");
Bellman-Ford算法时间复杂度是O(NM),比dijkstra复杂度还要高,还可以进行优化。实际操作中,该算法经常会在未达到n-1轮松弛前就计算出最短路,n-1其实是最大值。因此可以添加一个变量check来标记dis数组在本轮松弛中是否发生变化,如果没有发生变化,则可以提前跳出循环。代码如下:
#include<stdio.h> int main() { int d[10],i,k,n,m,u[10],v[10],w[10],check,flag; int inf=99999999; scanf("%d %d",n,m); for(i=1;i<=m;i++) scanf("%d %d %d",&u[i],&v[i],&w[i]); //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程 for(i=1;i<=n;i++) dis[i]=inf; dis[1]=0; //核心代码 for(k=1;k<=n-1;k++) { check=0;//用来标记在本轮松弛中数组dis是否会发生更新 for(i=1;i<=m;i++) if(dis[v[i]] > dis[u[i]+w[i]]) { dis[v[i]] = dis[u[i]+w[i]]; check=1; //数组dis发生更新,改变check的值 } //松弛完毕后检测数组dis是否更新 if(check==0) break;//如果数组dis没有更新,提前退出循环结束算法 } //检测负权回路 flag=0; for(i=1;i<=m;i++) if(dis[v[i]] > dis[u[i]+w[i]]) flag=1; if(flag==1) printf("此图必然含有负权回路"); else { for(i=1;i<=n;i++) printf("%d ",dis[i]); } getchar();getchar(); return 0; }
Bellman-Ford算法的优化:在每一次松弛操作后,就会有一些顶点已经求得其最短路,此后这些顶点的最短路的估计值就会一直保持不变,不再受后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。这就启发:每次仅对最短路径估计值发生变化了的顶点的所有出边执行松弛操作。
相关文章推荐
- python数据结构与算法——图的最短路径(Bellman-Ford算法)解决负权边
- 最短路径(三)—Bellman-Ford算法(解决负权边)
- Dijkstra算法,Bellman-Ford算法和BFS算法解决有向图的单源最短路径问题
- 队列优化并使用邻接表存储的Bellman-Ford算法模板解决最短路径存在负权边问题
- 负权边的最短路径--Bellman-Ford算法及其优化
- Bellman-Ford算法---求包含负权边单源最短路径(动态规划)
- 图的最短路径算法(四)--Bellman-Ford(解决负权边)单源点最短路径
- Bellman-Ford算法(求最短路径,并检测负权回路)
- 图的最短路径 Ford算法 解决负权问题
- Bellman-Ford-解决负权边的最短路径
- Bellman - Ford, SPFA 学习笔记(含有负权的单源最短路径)
- 最短路径Ⅱ—Bellman-Ford算法
- [ACM] POJ 3259 Wormholes (bellman-ford最短路径,判断是否存在负权回路)
- Bellman-ford算法应对有向图中有权值为负的情况下求最短路径
- 赚钱啦(单源最短路径(含有负权)Bellman_Forld()算法)
- 最短路径(Floyd算法和Dijkstra算法和Bellman-Ford算法)
- Bellman-ford算法 实现源点最短路径 允许路径中有负权值
- 最短路径Bellman-Ford算法
- 求任意权值最短路径的Bellman-Ford算法实现
- 求最短路径之Bellman-Ford算法