1. 程式人生 > >圖論演算法----最短路徑Bellman-Ford演算法詳解

圖論演算法----最短路徑Bellman-Ford演算法詳解

一、題目描述

最短路徑問題
題目描述
平面上有n個點(n<=100),每個點的座標均在-10000~10000之間。其中的一些點之間有連線。若有連線,則表示可從一個點到達另一個點,即兩點間有通路,通路的距離為兩點間的直線距離。現在的任務是找出從一點到另一點之間的最短路徑。

輸入
第1行:1個整數n
第2..n+1行:每行2個整數x和y,描述了一個點的座標
第n+2行:1個整數m,表示圖中連線的數量
接下來有m行,每行2個整數i和j,表示第i個點和第j個點之間有連線
最後1行:2個整數s和t,分別表示源點和目標點


輸出
第1行:1個浮點數,表示從s到t的最短路徑長度,保留2位小數


樣例輸入
5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5


樣例輸出
3.41

二、題目分析

(在看演算法分析時,先要保證自己對題目已經非常熟悉,)

這一道題有許多種方法來解決,但在這裡我講一下Bellman-Ford   O(NE)演算法。

這種演算法的思路非常簡單,運用了Dijkstra的藍白點思想,一開始認為起點為白點,其他的都是藍點,因為起點一定會與一些點相連,所以我們每一次列舉所有的邊,其中的一些邊就會連線白點和藍點,然後用所有的白點來修改藍點,來得到最短路徑。

但是,Ford演算法也有缺點,當有負權迴路時,求出的最短路徑將會報錯,因為有負權迴路的時候,我們會繞它走無數圈來得到最小的答案。

Bellman-Ford的改進方法是SPFA,就是用佇列來減少不必要的計算(以後再講)。

下面就是Bellman-Ford的程式碼

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
double zb[105][3],dis[105],w[5055];
int n,m,x,y,f[1005][3],s,t,i,j;
int main()
{
	scanf("%d",&n);//進行初始化
	for(i=1;i<=n;i++)
		scanf("%lf%lf",&zb[i][1],&zb[i][2]);
	scanf("%d",&m);
	for(i=1;i<=m;i++){
		dis[i]=1<<30;
		scanf("%d%d",&x,&y);
		f[i][1]=x;f[i][2]=y;
		w[i]=sqrt((zb[x][1]-zb[y][1])*(zb[x][1]-zb[y][1])+(zb[x][2]-zb[y][2])*(zb[x][2]-zb[y][2]));
	}
	scanf("%d%d",&s,&t);
	dis[s]=0;
	for(i=1;i<=n;i++){//演算法主題
		for(j=1;j<=m;j++){
			if(dis[f[j][1]]+w[j]<dis[f[j][2]]) dis[f[j][2]]=dis[f[j][1]]+w[j];
			if(dis[f[j][2]]+w[j]<dis[f[j][1]]) dis[f[j][1]]=dis[f[j][2]]+w[j];//因為是無向圖,所以要
		}//兩邊同時更新
	}
	printf("%.2lf",dis[t]);
}
當然,這一道題還有其他的解法,比如說FloyedDijkstra還有SPFA(Ford演算法的升級版),在這裡就不一一講述了。