最短路径之Bellman-Ford算法
Posted riotian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短路径之Bellman-Ford算法相关的知识,希望对你有一定的参考价值。
【最短路径】之Bellman-Ford算法
最短路径问题是图论研究中的一个经典算法问题,旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。算法具体的形式包括:
- 确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。适合使用Dijkstra算法。
- 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
- 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
- 全局最短路径问题 - 求图中所有的最短路径。适合使用Floyd-Warshall算法。
用于解决最短路径问题的算法被称做“最短路径算法”,有时被简称作“路径算法”。最常用的路径算法有:
- Dijkstra算法
- Bellman-Ford算法
- SPFA算法(Bellman-Ford算法的改进版本)
- Floyd-Warshall算法
- 深度或广度优先搜索算法(解决单源最短路径)
贝尔曼-福特算法与迪科斯彻算法类似,都以松弛操作为基础,即估计的最短路径值渐渐地被更加准确的值替代,直至得到最优解。在两个算法中,计算时每个边之间的估计距离值都比真实值大,并且被新找到路径的最小长度替代。
循环:
每次循环操作实际上是对相邻节点的访问,第(n)次循环操作保证了所有深度为n的路径最短。由于图的最短路径最长不会经过超过条(|V|-1)边,所以可知贝尔曼-福特算法所得为最短路径。
负边权操作
与迪科斯彻算法不同的是,迪科斯彻算法的基本操作“拓展”是在深度上寻路,而“松弛”操作则是在广度上寻路,这就确定了贝尔曼-福特算法可以对负边进行操作而不会影响结果。
负权环判定
因为负权环可以无限制的降低总花费,所以如果发现第(n)次操作仍可降低花销,就一定存在负权环。
参考代码:
/***先输入n,m,分别表结点数和边数,之后输入m个三元组,各表起点,终点,边权,输出1号结点到各结点的最短路径****/
#include <iostream>
#include <iomanip>
using namespace std;
#define nmax 1001
#define inf 99999999
int n, m, s[nmax], e[nmax], w[nmax], dst[nmax];
int main(){
while(cin >> n >> m && n != 0 && m != 0){
int i, j;
//初始化三个数组:起点数组s[],终点数组e[],权值数组w[],最短路径数组dst[]
for(i = 1; i <= m; i++)
cin >> s[i] >> e[i] >> w[i];
for(i = 1; i <= n; i++)
dst[i] = inf;
dst[1] = 0;
//使用Bellman_Ford算法
for(j = 1; j <= n-1; j++){
for(i = 1; i <= m; i++){
if(dst[e[i]] > dst[s[i]] + w[i])
dst[e[i]] = dst[s[i]] + w[i];
}
}
//测试是否有负权回路并输出
int flag = 0;
for(i = 1; i <= m; i++)
if(dst[e[i]] > dst[s[i]] + w[i])
flag = 1;
if(flag) cout << "此图含有负权回路
";
else{
for(i = 1; i <= n; i++){
if(i == 1)
cout << dst[i];
else
cout << setw(3) << dst[i];
}
cout << endl;
}
}
return 0;
}
优化
循环的提前跳出
在实际操作中,贝尔曼-福特算法经常会在未达到(|V|-1)次前就出解,(|V|-1)其实是最大值。于是可以在循环中设置判定,在某次循环不再进行松弛时,直接退出循环,进行负权环判定。
int SPFA(int s) {
queue<int> q;
bool inq[maxn] = {false};
for(int i = 1; i <= N; i++) dis[i] = 2147483647;
dis[s] = 0;
q.push(s); inq[s] = true;
while(!q.empty()) {
int x = q.front(); q.pop();
inq[x] = false;
for(int i = front[x]; i !=0 ; i = e[i].next) {
int k = e[i].v;
if(dis[k] > dis[x] + e[i].w) {
dis[k] = dis[x] + e[i].w;
if(!inq[k]) {
inq[k] = true;
q.push(k);
}
}
}
}
for(int i = 1; i <= N; i++) cout << dis[i] << ‘ ‘;
cout << endl;
return 0;
}
以上是关于最短路径之Bellman-Ford算法的主要内容,如果未能解决你的问题,请参考以下文章
最短路径之Dijkstra算法和Floyd-Warshall算法
(王道408考研数据结构)第六章图-第四节3:最短路径之BFS算法(思想代码演示答题规范)
(王道408考研数据结构)第六章图-第四节5:最短路径之弗洛伊德算法(思想代码演示答题规范)