Bellman-Ford 可解决带有负权边的最短路问题
解决负权边和Dijkstra相比是一个优点,Bellman-Ford的核心代码只有4行::
u[],v[],w[] 分别存一条边的顶点、权值,dis[]存从 1 源点到各个顶点的距离
for(i=1;i<=n-1;i++) for(j=1;j<=m;j++) if(dis[v[j]] > dis[u[j]]+w[j]) dis[v[j]] = dis[u[j]]+w[j];
愿过程:
循环n-1次,把每个顶点每条边都松弛;
优化方法:
①,最坏的情况就是循环了n-1次才求出到每个顶点的最短路径,若果在n-1次之前就已经全部松弛完成,那么后面的循环就是多余
优化:
for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可 { flag = 0; for(i=1;i<=m;i++)//对当前所有的边进行松弛 { if(dis[v[i]] > dis[u[i]]+w[i]) { dis[v[i]] = dis[u[i]]+w[i]; flag = 1; } } if(flag == 0) break;//松弛也完成,结束 }
②,原过程:每次循环松弛过后,都有已经确定了的源点到某点最短的路径,此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。
优化:确定一条边后总边数减一,把不能进行本次松弛的边再次后头存到原数组,松弛成功的边舍弃,再次松弛时只对未松弛的边进行操作,m的值会随着松弛预越来越小,直到全部完成。
for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可 { m = M;//重新赋值后的 m 是数组中存储边的条数 s = 1;flag = 0; for(i=1;i<=m;i++)//对当前所有的边进行松弛 { if(dis[v[i]] > dis[u[i]]+w[i]) { dis[v[i]] = dis[u[i]]+w[i]; M--; //松弛成功,边数减一 flag = 1; } else//把本次不能进行松弛的边重新存储到当前的数组 { u[s] = u[i]; v[s] = v[i]; w[s] = w[i]; s++; } } if(flag == 0) break;//松弛也完成,结束 }
附完整代码:
#include <stdio.h> int main() { int dis[10],i,k,m,n,s=1,u[10],v[10],w[10],M,flag; int inf = 99999999; scanf("%d%d",&n,&m); M = m; for(i=1;i<=m;i++) { scanf("%d%d%d",&u[i],&v[i],&w[i]);//输入各边及权值 } for(i=1;i<=n;i++) { dis[i] = inf;//初始化为正无穷 } dis[1] = 0;//以 1 为源点 for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可 { m = M;//重新赋值后的 m 是数组中存储边的条数 s = 1;flag = 0; for(i=1;i<=m;i++)//对当前所有的边进行松弛 { if(dis[v[i]] > dis[u[i]]+w[i]) { dis[v[i]] = dis[u[i]]+w[i]; M--; //松弛成功,边数减一 flag = 1; } else//把本次不能进行松弛的边重新存储到当前的数组 { u[s] = u[i]; v[s] = v[i]; w[s] = w[i]; s++; } } if(flag == 0) break;//松弛也完成,结束 } for(i=1;i<=n;i++) { printf("%d ",dis[i]); } return 0; }
测试数据1:
5 5 2 3 2 1 2 -3 1 5 5 4 5 2 3 4 3
运行结果:
0 -3 -1 2 4
测试数据2:
5 7 1 2 2 1 5 10 2 3 3 2 5 7 3 4 4 4 5 5 5 3 6
运行结果:
0 2 5 9 9