最短路径之Bellman-Ford算法

【最短路径】之Bellman-Ford算法


最短路径问题是图论研究中的一个经典算法问题,旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。算法具体的形式包括:

  • 确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。适合使用Dijkstra算法
  • 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
  • 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
  • 全局最短路径问题 - 求图中所有的最短路径。适合使用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 << "此图含有负权回路\n";
		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;
}

猜你喜欢

转载自www.cnblogs.com/RioTian/p/12597688.html