1. 程式人生 > 其它 >Bellman-Ford最短路

Bellman-Ford最短路

題目描述

給定一個n個點m條邊的有向圖,圖中可能存在重邊和自環,邊權可能為負數。

請你求出從1號點到n號點的最多經過k條邊的最短距離,如果無法從1號點走到n號點,輸出impossible

注意:圖中可能存在負權迴路。

【輸入格式】

第一行包含三個整數n,m,k。

接下來m行,每行包含三個整數x,y,z,表示存在一條從點x到點y的有向邊,邊長為z。

【輸出格式】

輸出一個整數,表示從1號點到n號點的最多經過k條邊的最短距離。

如果不存在滿足條件的路徑,則輸出impossible

【資料範圍】

1≤n,k≤500,
1≤m≤10000,
任意邊長的絕對值不超過10000。

【輸入樣例】

3 3 1
1 2 1
2 3 1
1 3 3

【輸出樣例】

3

由於含有負權邊,一定不可以使用Dijkstra。

Bellman-Ford演算法的實現方式是每一次都對圖中的所有邊進行一次鬆弛操作,對於邊(u,v):dist[v] = min(dist[v],dist[u] + w),每一輪都對邊進行操作,當某一輪沒有邊再發生鬆弛操作即可停止。

在最短路存在的前提下,那麼我們每一次的鬆弛操作都應當讓最短路的邊數+1,而最短路最多隻有n-1條邊,故最多迴圈n-1次,即可得出結果,而每次對邊進行鬆弛時間複雜度是O(m),故總時間複雜度是O(nm)。

Bellman-Ford演算法還可以檢測圖中是否存在負權環,當迴圈鬆弛操作n-1次後,第n次操作仍然有邊發生了鬆弛操作,那麼就意味著n個點的最短路可以擁有n條邊,根據抽屜定理,這條路徑中必然有某兩個點是相同,但一般判斷負環不使用Bellman-Ford演算法,而是用優化後的版本,SPFA演算法,包括求最短路也是,那麼Bellman-Ford演算法還有什麼優勢呢?優勢就在於本題,有邊數限制的最短路問題只能用Bellman-Ford演算法來求解

需要注意的點:

①由於是每一次對邊進行鬆弛操作,因此儲存圖的方式可以多變,只要能夠每次都遍歷完所有邊即可,故用結構體存下所有的邊會比鄰接表書寫起來更方便;

②每次鬆弛的時候,是使用上次鬆弛完的結果來計算本次的結果,因此計算的時候需要備份一次上次的結果,以免發生“串聯更新”的問題,也就是使用本次鬆弛計算的結果來更新後續的結果;

③輸出答案時,可能存在負權邊更新了兩個無法到達的點的情況,所以判斷不能直接判斷是否等於0x3f,比如1無法到達點5,也無法到達點7,但5->7的邊權是-2,那麼在每次鬆弛的時候,是會導致這個值變小一點點的。

 1 #include <iostream>
 2
#include <string.h> 3 using namespace std; 4 5 const int N = 501,M = 10009; 6 int n,m,k; 7 int dist[N],backup[N]; 8 struct Edge 9 { 10 int a,b,w; 11 }edges[M]; 12 13 void BellmanFord() 14 { 15 memset(dist,0x3f,sizeof dist); 16 dist[1] = 0; 17 for(int i = 0;i < k;++i)//限制k條邊則鬆弛k次 18 { 19 memcpy(backup,dist,sizeof dist); 20 for(int j = 0;j < m;++j) 21 { 22 int a = edges[j].a; 23 int b = edges[j].b; 24 int w = edges[j].w; 25 dist[b] = min(dist[b],backup[a] + w); 26 } 27 } 28 if(dist[n] < 0x3f3f3f3f/2) 29 cout << dist[n] << endl; 30 else 31 cout << "impossible" << endl; 32 } 33 34 int main() 35 { 36 cin >> n >> m >> k; 37 for(int i = 0;i < m;++i) 38 { 39 int a,b,w; 40 cin >> a >> b >> w; 41 edges[i] = {a,b,w}; 42 } 43 44 BellmanFord(); 45 46 return 0; 47 }