最短路徑是三種求法

2021-08-17 12:37:57 字數 3730 閱讀 3178

說是總結,其實自己也沒有學多長時間只是把自己這段時間的一些經驗總結下來,用來供後來的初學者漲點經驗吧。對於學習演算法,個人的理解就是首先要去理解演算法的本質,然後想想演算法的實現過程,如何用**去描述這個演算法,然後就是去記模板了(對於像我這種初學者來說,這一步其實蠻重要的)。另外說下做最短路問題的一些容易出錯的地方。1、要小心重邊,就是題目會給你一些邊類似於2 4 5,2 4 3;這種邊和權值的。2、要注意圖中所給的是有向圖還是無向圖,一般情況下都是無向圖的,如果有特殊說明的話,就是有向圖。接下來我就以hdu的1874--暢通工程續這道題來簡單分析一下這三種演算法。

第一、dijkstra演算法:這種演算法只能解決權值不是負的圖(稍後解釋一下為什麼權值不能為負)。

特點:可以求出單源點到其他頂點的最短距離,演算法的複雜程度比floyd演算法稍微低一些。

演算法描述:比較類似於求最小生成樹中的prime演算法,設定乙個點集合s,然後用貪心的方式去選擇擴充這個集合。就是說如果1到3的距離為5,1到2為1,同時2到3為2;那麼這個時候dis[3]應該=3;即用dis陣列來儲存每個點到遠點的最短距離,並不斷更新這個最短距離,當所有的點都加入了集合s時就可以跳出迴圈了。

缺點:不能解決帶負權值的圖,如果圖很大的話也不是很好處理,可能會需要用鄰接表,這個沒試過,圖很大的話一般都用spfa了。為什麼不能解決負權值問題?歸入s集

合的節點的最短路徑及其長度不再變更,如果邊上的權值允許為負值,那麼有可能出現當與s

內某點(記為a)以負邊相連的點(記為b)確定其最短路徑時,它的最短路徑長度加上這條負邊的權值,結果小於a原先確定的最短路徑長度,而此時a在dijkstra演算法下是無法更新的,由此便可能得不到正確的結果。

(標記一下,醒目些)。

**實現的過程(用hdu--1874--暢通工程續--來舉例,題目自搜哦)

[cpp]

view plain

copy

#include

#include

#define inf 0x3f3f3f3f

intvis[210],map[210][210],dis[210],n,m,beg,end;  

void

init()  

}  void

dijkstra()  

if(dis[end]!=inf)  

printf("%d\n"

,dis[end]);  

else

printf("-1\n"

);  

}  int

main()  

scanf("%d%d"

,&beg,&end);  

dijkstra();  

}  return

0;  

}  

第二、就是spfa演算法,這個是金牌陳給講的,用了佇列和鄰接表(現在都不知道鄰接表是何物,默默地記了個模板)

特點:也是用來求單源最短路徑的,不過比dij稍微好一些的就是權值可正可負,不過如果是負權值的話需要判斷一下是否有負環存在。也可以用來做判斷負環的題。解釋下負環,點與點之間相互連線構成乙個環,並且該環的總權值為負,那麼這就是乙個負環。(重要的事情要標記。)關於負環問題可參照poj --3259--warmholes,我部落格裡有詳解。

演算法介紹:演算法介紹:建立乙個佇列q,初始時佇列裡只有乙個起始點,在建立乙個陣列dis記錄起始點到所有點的最短路徑,並且初始化這個陣列。然後進行鬆弛操作,用 佇列裡面的點去重新整理起始點到所有點的最短路,如果重新整理成功且重新整理點不再佇列中則把該點加入到佇列最後,重複執行直到隊列為空。如果存在負環的話,需要建立乙個陣列來判斷每個點進入佇列了多少次,否則佇列一直都不為空。

**實現(還是dijk上面說的那道題為例ps:沒有考慮負環問題,負環問題可以參考poj--3259,部落格裡面有)

[cpp]

view plain

copy

#include

#include

#include

#define n 210

#define m 2010

#define inf 0x3f3f3f3f//定義無窮大 

using

namespace

std;  

intdis[n],vis[n],head[n],n,m,edgenum;  

struct

nodeedge[m];  

void

init()  

void

add(

intu,

intv,

intw);  

edge[edgenum]=e;  

head[u]=edgenum++;  

}  void

spfa(

intbeg,

intend)  

}  }  

}  if

(dis[end]==inf)  

printf("-1\n"

);  

else

printf("%d\n"

,dis[end]);  

}  int

main()  

intbeg,end;  

scanf("%d%d"

,&beg,&end);  

spfa(beg,end);  

}  return

0;  

}  

第三、floyd演算法。

特點:可以求多源最短路,權值同樣不能為負。

1 floyd 的思想就是通過列舉n個點利用dp的思想來更新最短距離的,假設當前列舉到第k個點,那麼就有任意的兩個點i , j ,如果i k 相連 j k 相連 那麼就可以知道這個時候dis[i][j] = min(dis[i][j] , dis[i][k] + dis[k][j]);,那麼只要列舉完n個點,那麼就說明已經完全更新完所有兩點直間的最短路。

2 floyd演算法是最簡單的最短路徑的演算法,可以計算圖中任意兩點間的最短路徑。floyd演算法的時間複雜度為o(n^3),如果是乙個沒有邊權的圖,把相連的兩點間的距離設為dis[i][j]=1.不相連的兩點設為無窮大,用floyd演算法可以判斷i j兩點是否相連。

3 floyd 演算法不允許所有的權值為負的迴路。可以求出任意兩點之間的最短距離。處理的是無向圖

4 缺點是時間複雜度比較高,不適合計算大量資料

5 如果dis[i][i] != 0,說明此時存在環(這種題目前為止還沒見到過,也沒考慮過,日後再深度剖析)。 

6 如果利用floyd求最小值的時候,初始化dis為inf , 如果是求最大值的話初始化為-1.

**實現(還是那道題)

[cpp]

view plain

copy

#include

#include

#define inf 0x3f3f3f3f

intdis[300][300],n;  

void

init()  

void

floyd()  

}  int

main()  

floyd();  

intbeg,end;  

scanf("%d%d"

,&beg,&end);

//可以求任意兩個點之間的最短路。 

if(dis[beg][end]==inf)  

printf("-1\n"

);  

else

printf("%d\n"

,dis[beg][end]);  

}  return

0;  

}  

最短路徑的三種實現方法

helpbuptguoan created by new life on 16 8 24.include include include include include include include using namespace std define n 5 圖的大小 const int typ...

hdu2544 最短路徑 三種演算法

複雜度 o e lo ac include include include include include include using namespace std include const int maxn 105 const int inf 1000000 map 陣列模擬鄰接表 int vex...

單源最短路徑的求法

2.最短路徑 演算法思想 設圖中有n個結點,設定乙個集會u,存放已經求出最短路徑的結點 初始時u中的元素是源點 v u是尚未確定最短路徑的頂點的集合。每次從v u集合中找這樣乙個結點best j best j是u集合中結點的鄰接點,到源點的距離最短 等於到父結點的距離加上父結點到源點的距離 然後把該...