最短路徑 Floyd演算法 Dijkstra演算法 Bellman-Ford(貝爾曼)演算法
相信大家應該對最短路徑演算法很感興趣吧!不感興趣也沒關係,我們一起來看看下面的例子。最短路徑應該是在眾多演算法中。最常用的一類演算法。為什麼這樣說呢?? 例如: 1.乘汽車旅行的人總希望找出到目的地的儘可能的短的行程。如果有一張地圖並在圖上標出每對十字路口之間的距離,如何找出這一最短行程? 2.某省自從實行了很多年的暢通工程計劃後,終於修建了很多路。不過路多了也不好,每次要從一個城鎮到另一個城鎮時,都有許多種道路方案可以選擇,而某些方案要比另一些方案行走的距離要短很多。這讓行人很困擾。 現在,已知起點和終點,請你計算出要從起點到終點,最短需要行走多少距離。 那麼如果想要求解這些問題,那麼這裡就會用到最短路徑。 一:Floyd演算法: Floyd演算法是一種求任意點到任意點的最短距離。可以求邊權為負值,有向圖、無向圖等的最短路徑。但是邊權可以有負權值的邊,但不能有包含負權值邊組成的迴路,不然算出來的就不是正確答案!!這個演算法效率很低可以說是暴力。但是這種暴力是帶著動態規劃的暴力!! 那麼為什麼邊權可以為負值,但是卻不能有邊權和為負值的迴路呢??這是一個值得深思的問題。 問得好,這個問題是一個好問題。但是呢?我先把問題放在這!看過程式碼後我在給大家一組資料就可知道!!!
/*
floyd演算法
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Inf 1<<29
int mp[100][100];
int n,m;
int main(){
while(~scanf("%d%d",&n,&m)){//代表有n個節點 m 條權值邊
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if (i!=j){//初始化預設距離表示i節點到j節點的距離。節點相同距離為0
mp[i][j]=Inf;
}else{
mp[i][j]=0;
}
}
}
int a,b,c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
mp[a][b]=c;
// mp[b][a]=c;//寫上這個就是無向圖
}
for(int k=1;k<=n;k++){//主要程式碼實現區,可以模擬一下。關鍵是模擬,然後自己再想為什麼。。
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j&&j!=k){
mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){//在這裡輸出任意點到任意點的距離
printf("dis %d %d = %d\n",i,j,mp[i][j]);
}
}
}
return 0;
}
模擬完後,我就要告訴你這個演算法的特點是什麼和該注意什麼?這個演算法的時間複查度是o(n^3) 是很大的稍微不注意就會超時。當然除了水題。。。它的目的是算任意節點到任意節點的距離,上面也提到過了。 這裡最需要注意的就是為什麼這個演算法能求出含有負節點卻不能算出含有負迴路的圖??只要是含有負迴路,那麼正確結果就是 負無窮。之所以能求含有負節點主要是因為 mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]); 的關係。其實不論是否為正數或者為負數。都是有大小的。讓每步都最小,那麼結果就最小。 二.Dijkstra演算法 Dijkstra演算法也是算最短路徑的。它是隻能算單源最短路徑。當然你也可以把模板改了。把所有節點都遍歷一遍也就成了算多源的模板。Dijkstra演算法的主導思想是首先儲存起點(你指定的點)到任意節點的距離,然後在這些距離中找一個最小值。畢竟你是求最短路徑,肯定就是找最小的了。但是這種實現是根據動態規劃實現。然後把你找到的點加入到起點中,我們這裡的加入用的是標記。然後更新所有節點到起點的距離。然後重複就可以算出答案。 下面來看程式碼:
//Dijkstra演算法
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Inf 1<<29
using namespace std;
int mp[100][100];
int dis[100],vis[100];
int n,m;
int dij(int s){
for(int i=1;i<=n;i++){
vis[i]=0;
dis[i]=mp[s][i];//初始化1節點到任意節點的距離
}
vis[s]=1;
dis[s]=0;//其實前面已經初始化過了,可以不寫。
for(int i=1;i<n;i++){
int to=-1;
int d=Inf;
for(int j=1;j<=n;j++){
if(!vis[j]&&d>dis[j]){//如果j節點還沒加入其實節點,並且1節點及加入1節點的節點到j節點有距離
d=dis[j];
to=j;
}
}
vis[to]=1;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]>dis[to]+mp[to][j]){
dis[j]=dis[to]+mp[to][j];//更新1節點到任意節點的距離。
}
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){//初始化預設距離表示i節點到j節點的距離。節點相同距離為0
for(int j=1;j<=n;j++){
if(i!=j){
mp[i][j]=Inf;
}else{
mp[i][j]=0;
}
}
}
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);//輸入實際a點到b點的距離
mp[a][b]=c;
// mp[b][a]=c;//寫上這個就是無向圖
}
dij(1);//這裡的1(數字1)表示我要求1這個節點到所有節點的最短距離!!!
for(int i=2;i<=n;i++){
printf("-->i=%d dis[%d]=%d\n\n",i,i,dis[i]);//dis中儲存的表示1節點到任意節點的距離,當i=2時表示1節點到2節點的距離
}
}
return 0;
}
這裡對於Dijkstra演算法有需要注意的地方,那就是Dijkstra演算法不能算含有負節點的圖。當然更不可能算含有負節點回路的圖。為什麼呢??
我們這裡就拿有向圖來解答。看圖片輸入測試資料 3 3 1 3 1 1 2 2 2 3 -5 執行出來A-->B=2 A-->C=1肯定這樣就錯了應該是A-->B=2 A-->C=-3才對。 這也跟程式碼有關係因為這個程式碼的思想是貪心。區域性貪心。所以會造成這樣的結果。 三.Bellman-Ford演算法 那麼算單源最短路徑Dijkstra演算法不能算含有負值節點的並且也不能算含有負節點回路的。那麼是否有其他演算法呢??答案是有的,那就是Bellman-Ford演算法,
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Inf 1<<29
struct node{
int u,v,w;
}tu[1000];
using namespace std;
int n,m,dis[100];
int bellman_floyd(int s){
for(int i=1;i<=n;i++){
dis[i]=Inf;
}
dis[s]=0;
for(int i=1;i<n;i++){//思考這裡為什麼是n-1次迴圈。
for(int j=1;j<=2*m;j++){
if(dis[tu[j].u]+tu[j].w<dis[tu[j].v]){
dis[tu[j].v]=tu[j].w+dis[tu[j].u];//鬆弛操作。
}
}
}
for(int i=1;i<=2*m;i++){
if(dis[tu[i].u]+tu[i].w<dis[tu[i].v])//判斷是否還能鬆弛,如果能就含有負值迴路。
return -1;
}
return 1;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<2*m;i+=2){
int a,b,c;
scanf("%d%d%d",&tu[i].u,&tu[i].v,&tu[i].w);
tu[i+1].u=tu[i].v;
tu[i+1].v=tu[i].u;
tu[i+1].w=tu[i].w;
}
int x= bellman_floyd(1);
if(x==-1){
printf("no\n");
}else{
for(int i=1;i<=n;i++){
printf("dis[%d]=%d\n",i,dis[i]);
}
}
}
return 0;
}
為什麼是n-1次鬆弛操作。
考慮:為什麼要迴圈n-1次?
答:因為最短路徑肯定是個簡單路徑,不可能包含迴路的,
如果包含迴路,且迴路的權值和為正的,那麼去掉這個迴路,可以得到更短的路徑
如果迴路的權值是負的,那麼肯定沒有解了
圖有n個點,又不能有迴路
所以最短路徑最多n-1邊
又因為每次迴圈,至少relax一邊
所以最多n-1次就行了
我們來舉個例項。
6 5
5 6 1
4 5 2
3 4 3
2 3 4
1 2 5
就是這組測試資料。這是最壞的一種情況了。
那麼為什麼不能有負值環路這是另外一組測試資料。
6 6
1 2 1
2 3 -1
3 4 -1
4 5 -1
5 2 -1
5 6 1
這組資料就能很好的說明。
總結:Floyd演算法能很好的解決稠密圖(V大,V代表節點數)。而Dijkstra演算法卻能很好的解決稀疏圖(E小,E代表邊權數)。而SPFA也能很好解決稀疏圖。(後面我會寫的)
好了就說到這裡了,如果有不足的地方請指正!!!謝謝!!
相關推薦
最短路徑 Floyd演算法 Dijkstra演算法 Bellman-Ford(貝爾曼)演算法
相信大家應該對最短路徑演算法很感興趣吧!不感興趣也沒關係,我們一起來看看下面的例子。最短路徑應該是在眾多演算法中。最常用的一類演算法。為什麼這樣說呢?? 例如: 1.乘汽車旅行的人總希望找出到目的地的儘可能的短的行
Bellman-ford(貝爾曼-福特演算法)解析
Dijkstra演算法是處理單源最短路徑的有效演算法,但它侷限於邊的權值非負的情況,若圖中出現權值為負的邊,Dijkstra演算法就會失效,求出的最短路徑就可能是錯的。 這時候,就需要使用其他的演算法來求解最短路徑,Bellman-Ford演算法就是其中最常用的一個。該
幾個最短路徑演算法Floyd、Dijkstra、Bellman-Ford、SPFA的比較
幾大最短路徑演算法比較July、二零一一年二月十二日。----------------------------------- 幾個最短路徑演算法的比較:Floyd求多源、無負權邊的最短路。用矩陣記錄圖。時效性較差,時間複雜度O(V^3)。 Floyd-
最短路徑-Floyd(弗洛伊德)演算法
最短路徑-Floyd(弗洛伊德)演算法 簡介: 相較Dijkstra,Floyd是一個完全窮舉圖中每個點到末尾點的最短路徑 演算法思想: 按慣例說兩個工具 Path[MAX_SIZE][MAX_SIZE]:儲存所有的最短路徑(指向
最短路徑—Floyd演算法
Floyd演算法: 1,從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權為無窮大。 2,對於每一對頂點 u 和 v,看看是否存在一個頂點 w 使得從 u 到 w 再到 v 比已知的路徑更短。如果是更新它。 Floyd-Warshall——
【資料結構】所有頂點對的最短路徑 Floyd演算法
所有頂點對的最短路徑問題是指:對於給定的有向圖G=(V,E),求任意一對頂點之間的最短路徑。 可以求解得到的 的遞推公式: #include <stdio.h> #include <stdlib.h> const int FINI
結點對最短路徑Floyd弗洛伊德演算法解析
暑假,小哼準備去一些城市旅遊。有些城市之間有公路,有些城市之間則沒有,如下圖。為了節省經費以及方便計劃旅程,小哼希望在出發之前知道任意兩個城市之前的最短路程。 上圖中有4個城市8條公路,公路上的數字表示這條公路的長短。請注意這些公
圖-最短路徑-Floyd演算法
給定高鐵的規劃方案,如何求任意兩點的最短路呢? 方法是Floyd演算法。 演算法思想: 1、計算從i出發,跳點為空,直接到j 的最短路。記做D[i][j]。 2、可選跳點為1時,i到j的路徑分為兩種情況: 或者不經過1, 此時前者最短為D[i][j], 或者經過1
最短路徑---Floyd演算法(C++)
Floyd演算法的介紹 演算法的特點: 弗洛伊德演算法是解決任意兩點間的最短路徑的一種演算法,可以正確處理有向圖或有向圖或負權(但不可存在負權迴路)的最短路徑問題,同時也被用於計算有向圖的傳遞閉包。 演算法的思路 通過Floyd計算圖G=(V,E)中各個頂點的最短路徑時,需要引入
多源最短路徑--Floyd演算法
#include<iostream> #include<cstdio> using namespace std; const int INF = 0x3f3f3f3f; int main(void) { int e[10][10] = { 0 }, dis[10], boo
單源最短路徑問題之dijkstra演算法
歡迎探討,如有錯誤敬請指正 如需轉載,請註明出處 http://www.cnblogs.com/nullzx/ 1. 演算法的原理 以源點開始,以源點相連的頂點作為向外延伸的頂點,在所有這些向外延伸的頂點中選擇距源點最近的頂點(如果有多個距離最近的頂點,任意選擇一個即可)繼續向四周延伸(某個頂點被選作繼續
單源最短路徑的迪克斯特拉(Dijkstra)演算法
Dijkstra演算法1.定義概覽Dijkstra(迪傑斯特拉)演算法是典型的單源最短路徑演算法,用於計算一個節點(節點需為源點)到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。Dijkstra演算法是很有代表性的最短路徑演算法,注
最短路徑(鄰接矩陣)-Dijkstra演算法
Dijkstra演算法又叫作迪傑斯特拉演算法,是利用"貪心法"(在對問題進行求解時,總是做出在當前看來最好的選擇策略)設計演算法的一個成功範例。 適用條件:帶權無環和無負權值 舉個栗子: Dijkstra演算法的程式碼實現如下: pa
C++ 求最短路徑問題之Dijkstra演算法(一)
求最短路徑之Dijkstra演算法 Dijkstra演算法是用來求單源最短路徑問題,即給定圖G和起點s,通過演算法得到s到達其他每個頂點的最短距離。 基本思想:對圖G(V,E)設定集合S,存放已被訪問的頂點,然後每次從集合V-S中選擇與起點s的最短距離最小的一個頂點(記為u
最短路徑演算法—Bellman-Ford(poj1860 currency exchange)
Bellman-Ford演算法詳講Dijkstra演算法是處理單源最短路徑的有效演算法,但它侷限於邊的權值非負的情況,若圖中出現權值為負的邊,Dijkstra演算法就會失效,求出的最短路徑就可能是錯的。這時候,就需要使用其他的演算法來求解最短路徑,Bellman-Ford演算
【圖】最短路徑:迪傑斯特拉(Dijkstra)演算法
網圖和非網圖中,最短路徑的含義不同: 非網圖中,因為沒有邊上的權值,最短路徑指的是兩頂點之間經過的邊數最少的路徑; 網圖中,最短路徑指的是兩頂點之間經過的邊上權值之和最少的路徑,並且稱路徑上的第一個
經典演算法之圖的最短路徑(一):Dijkstra演算法
Dijkstra演算法可以說基本上每一本有講到圖的最短路徑的書上都會有的一個演算法,但基本上都是講原理和虛擬碼,今天自己用Java程式碼給實現了一下,記錄在此。 Dijkstra演算法只是解決某些圖的最短路徑問題,這些圖需要滿足以下條件:權值非負、有向圖。並且該演算法只適用
每一對頂點之間的最短路徑----Floyd演算法----(附完整程式碼)
1.Floyd演算法 2.輸出每兩對頂點之間的最短距離 #include<stdio.h> #include<stdlib.h> #define MaxVertexNum 100 #define INFINITY 65535 //#define MaxSize 1
最短路徑-Floyd演算法的matlab實現.md
最短路徑-Floyd演算法的matlab實現 弗洛伊德演算法是解決任意兩點間的最短路徑的一種演算法,可以正確處理有向圖或有向圖或負權(但不可存在負權迴路)的最短路徑問題。 在Floyd演算法中一般有兩個矩陣,一個距離矩陣D,一個路由矩陣R,其中距離矩陣用
最短路徑Floyd演算法具體演示
演示作品下載 最短路徑Floyd演算法具體演示 ----用VC++來實現路由選擇演算法 摘要: 確定圖的路由選擇的策略要考慮很多技術因素。其包括:選擇最短路由還是最佳路由;