当前位置: 代码迷 >> 综合 >> Bellman-Ford算法(限制边权的最短路、负权回路)
  详细解决方案

Bellman-Ford算法(限制边权的最短路、负权回路)

热度:94   发布时间:2023-11-26 23:55:22.0

算法思想

Bellman-Ford算法构造一个最短路径长度数组序列dist1[u], dist2[u], …, distn-1[u]。其中: 

 dist1[u]为从源点v到终点u的只经过一条边的最短路径长度,并有dist1[u]=Edge[v][u]; 

 dist2[u]为从源点v最多经过两条边到达终点u的最短路径长度; 

 dist3[u]为从源点v出发最多经过不构成负权值回路的三条边到达终点u的最短路径长度; 

 …… 

distn-1[u]为从源点v出发最多经过不构成负权值回路的n-1条边到达终点u的最短路径长 

度; 

算法的最终目的是计算出distn-1[u],为源点v到顶点u的最短路径长度。

dist k [u]的计算

采用递推方式计算distk[u]: 

1、 设已经求出 distk-1[u]

2、从图的邻接矩阵找到各个顶点j到达顶点u的距离Edge[ j][u],计算 min{distk-1[j]+Edge[j][u]},可得从源点v经过各个顶点,最多经过不构成负权值回路的k条边到达终点u的最短路径的长度; 

2、比较distk-1[u]和min{distk-1[j]+Edge[j][u]} ,取较小者作为distk[u]的值。

 

Bellman算法和dijkstra算法的区别 

Dijkstra算法在求解过程中,源点到集合S内各顶点的最短路径一旦求出,之后就不变了,修改的仅仅是源点到T集合中各顶点的最短路径长度。 

Bellman算法在求解过程中,每次循环都要修改所有顶点的dist[ ],源点到各顶点最短路径长度一直要到Bellman算法结束才能确定。

Bellman算法判断负权回路思想

如果存在从源点可达的负权值回路,则最短路径不存在,因为可以重复走这个回路,使得路径无穷小。 

在Bellman算法中判断是否存在从源点可达的负权值回路的方法: 

在求出distn-1[ ]之后,再对每条边<u,v>判断一下(即distn[ ]):加入这条边是否会使得顶点v的最短路径再缩短,即判断:dist[u]+Edge(u,v)<dist[v]是否成立,如果成立,则说明存在从源点可达的负权值回路。

  

 

for (i=0;i<n;i++) { for (j=0;j<n;j++) { if (Edge[i][j]<MAX && dist[j]>dist[i]+Edge[i][j]) return 0;//存在从源点可达的负权值回路 } 
}
return 1;

 假设源为k,当i = k且j = k时判断了dist(k->k)的路径。是否必要呢?

答案是必要!若存在负权回路,源点的距离也可能缩短

复杂度分析

n个顶点e条边,三重for循环

(1)使用邻接矩阵O(n^2)

  (2)  使用邻接表,最多考查每条边 n 次,O(ne) 

#include<iostream>
#include<algorithm>
#include<cstring>
const int M = 1e4 + 10, N = 510;
using namespace std;
int dist[N];
int last[N];
int n, m, k;struct Edge
{int a, b, c;
}edges[M];void bellman_fold()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;for(int i = 0; i < k; i++){memcpy(last, dist, sizeof dist);for(int j = 0; j < m; j++){auto e = edges[j];dist[e.b] = min(dist[e.b], last[e.a] + e.c);//}}
}int main()
{cin >> n >> m >> k;for(int i = 0; i < m; i++){int a, b, c;cin >> a >> b >> c;edges[i] = {a, b, c};}bellman_fold();if(dist[n] > 0x3f3f3f3f / 2) puts("impossible");else cout << dist[n] << endl;return 0;
}
  相关解决方案