單元最短路徑 SPFA演算法

2021-08-08 02:40:37 字數 4484 閱讀 1357

(2012-09-16 19:12:37)

標籤:分類:圖論

介紹:

單源最短路徑的演算法最常用的是dijkstra,些演算法從時間複雜度來說為o(n^2),但是面對含有負權值的圖來說就無能為力了,此時dellman-ford演算法就有用了,這咱演算法是採用的是動態規化的思想,但是2023年西南交通大學段凡丁發表了spfa(shortest path faster algorithm)聽這個名字就懂了,這種演算法在時間上一定很快了。它是對dellman-ford的優化,所以建議今後直接學spfa。很多時候,給定的圖存在負權邊,這時類似dijkstra等演算法便沒有了用武之地,而bellman-ford演算法的複雜度又過高,spfa演算法便派上用場了。

補:然而實際上演算法時間複雜度很不穩定,有時候k可以達到指數級的,在稀疏圖上表現相對更好(因為是靠邊去推進的,而dijkstra是靠點)

思路:

簡潔起見,我們約定有向加權圖g不存在負權迴路,即最短路徑一定存在。當然,我們可以在執行該演算法前做一次拓撲排序,以判斷是否存在負權迴路,但這不是我們討論的重點。

和上文一樣,我們用陣列d記錄每個結點的最短路徑估計值,而且用鄰接表來儲存圖g。我們採取的方法是動態逼近法:設立乙個先進先出的佇列用來儲存待優化的結點,優化時每次取出隊首結點u,並且用u點當前的最短路徑估計值對離開u點所指向的結點v進行鬆弛操作,如果v點的最短路徑估計值有所調整,且 v點不在當前的佇列中,就將v點放入隊尾。這樣不斷從佇列中取出結點來進行鬆弛操作,直至佇列空為止。

定理3 只要最短路徑存在,上述spfa演算法必定能求出最小值。

證明:每次將點放入隊尾,都是經過鬆弛操作達到的。換言之,每次的優化將會有某個點v的最短路徑估計值d[v]變小。所以演算法的執行會使d越來越小。由於我們假定圖中不存在負權迴路,所以每個結點都有最短路徑值。因此,演算法不會無限執行下去,隨著d值的逐漸變小,直到到達最短路徑值時,演算法結束,這時的最短路徑估計值就是對應結點的最短路徑值。(證畢)

剛才我們只是籠統地說spfa演算法在效率上有過人之處,那麼到底它的複雜度是怎樣的?

定理4 在平均情況下,spfa演算法的期望時間複雜度為o(e)。

證明:上述演算法每次取出隊首結點u,並訪問u的所有臨結點的複雜度為o(d),其中d為點u的出度。運用均攤分析的思想,對於|v|個點|e|條邊的圖,點的平均出度為,所以每處理乙個點的複雜度為o( )。假設結點入隊的次數h,顯然h隨圖的不同而不同。但它僅與邊的權值分布有關。我們設 h=kv,則演算法spfa的時間複雜度為。在平均的情況下,可以將k看成乙個比較小的常數,所以spfa演算法在一般情況下的時間複雜度為o(e)。(證畢)

聰明的讀者一定發現了,spfa和經過簡單優化的bellman-ford無論在思想上還是在複雜度上都有相似之處。確實如此。兩者的思想都屬於標號修正的範疇。演算法是迭代式的,最短路徑的估計值都是臨時的。演算法思想是不斷地逼近最優解,只在最後一步才確定想要的結果。但是他們實現的方式上存在差異。正因為如此,它們的時間複雜度其實有較大差異的。在bellman-ford演算法中,要是某個點的最短路徑估計值更新了,那麼我們必須對所有邊指向的終點再做一次鬆弛操作;在spfa演算法中,某個點的最短路徑估計值更新,只有以該點為起點的邊指向的終點需要再做一次鬆弛操作。在極端情況下,後者的效率將是前者的n倍,一般情況下,後者的效率也比前者高出不少。基於兩者在思想上的相似,可以這樣說,spfa演算法其實是bellman-ford演算法的乙個進一步優化的版本。

演算法流程

演算法大致流程是用乙個佇列來進行維護。初始時將源加入佇列。每次從佇列中取出乙個元素,並對所有與他相鄰的點進行鬆弛,若某個相鄰的點鬆弛成功,則將其入隊。直到隊列為空時演算法結束。

這個演算法,簡單的說就是佇列優化的bellman-ford,利用了每個點不會更新次數太多的特點發明的此演算法

spfa——shortest path faster algorithm,它可以在o(ke)的時間複雜度內求出源點到其他所有點的最短路徑,可以處理負邊。spfa的實現甚至比dijkstra或者bellman_ford還要簡單:

設dist代表s到i點的當前最短距離,fa代表s到i的當前最短路徑中i點之前的乙個點的編號。開始時dist全部為+∞,只有dist[s]=0,fa全部為0。

維護乙個佇列,裡面存放所有需要進行迭代的點。初始時佇列中只有乙個點s。用乙個布林陣列記錄每個點是否處在佇列中。

每次迭代,取出隊頭的點v,依次列舉從v出發的邊v->u,設邊的長度為len,判斷dist[v]+len是否小於 dist[u],若小於則改進dist[u],將fa[u]記為v,並且由於s到u的最短距離變小了,有可能u可以改進其它的點,所以若u不在佇列中,就將它放入隊尾。這樣一直迭代下去直到佇列變空,也就是s到所有的最短距離都確定下來,結束演算法。若乙個點入隊次數超過n,則有負權環。

演算法訓練 最短路  

時間限制:1.0s   記憶體限制:256.0mb

問題描述

給定乙個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。

輸入格式

第一行兩個整數n, m。

接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。

輸出格式

共n-1行,第i行表示1號點到i+1號點的最短路。

樣例輸入

3 31 2 -1

2 3 -1

3 1 2

樣例輸出

-1-2

資料規模與約定

對於10%的資料,n = 2,m = 2。

對於30%的資料,n <= 5,m <= 10。

對於100%的資料,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其他所有頂點。

題目分析:再記一下spfa的vector版的板子

[cpp]view plain

copy

#include 

#include 

#include 

#include 

using

namespace

std;  

intconst

max = 200005;  

intconst

inf = 1 << 30;  

intn, m;  

struct

edge  

e[max << 2];  

struct

node  

};  

vector vt[max];  

intdis[max];  

bool

vis[max];  

void

spfa(

intv0)  

}  }  }  

}  int

main()    

演算法訓練 最短路  

時間限制:1.0s   記憶體限制:256.0mb

問題描述

給定乙個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。

輸入格式

第一行兩個整數n, m。

接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。

輸出格式

共n-1行,第i行表示1號點到i+1號點的最短路。

樣例輸入

3 31 2 -1

2 3 -1

3 1 2

樣例輸出

-1-2

資料規模與約定

對於10%的資料,n = 2,m = 2。

對於30%的資料,n <= 5,m <= 10。

對於100%的資料,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其他所有頂點。

題目分析:再記一下spfa的vector版的板子

[cpp]view plain

copy

#include 

#include 

#include 

#include 

using

namespace

std;  

intconst

max = 200005;  

intconst

inf = 1 << 30;  

intn, m;  

struct

edge  

e[max << 2];  

struct

node  

};  

vector vt[max];  

intdis[max];  

bool

vis[max];  

void

spfa(

intv0)  

}  }  }  

}  int

main()    

單元最短路徑 spfa

時間複雜度最差為o mn 與bellman ford相同 適用範圍 負權的單源最短路,或者判斷負環。稀疏圖 稠密圖用dijkstra演算法 可以記錄最短路到目標的路徑。原理 將起點放入佇列,用隊首的點不斷更新所連邊上的點,更新後將未在佇列中的放入佇列,直到隊列為空。若乙個點被放入佇列超過n 點的個數...

最短路徑演算法 SPFA

求最短路徑的演算法有許多種,除了排序外,恐怕是oi界中解決同一類問題演算法最多的了。最熟悉的無疑是dijkstra,接著是bellman ford,它們都可以求出由乙個源點向其他各點的最短路徑 如果我們想要求出每一對頂點之間的最短路徑的話,還可以用floyd warshall。spfa是這篇日誌要寫...

SPFA演算法 最短路徑

只要最短路徑存在,spfa演算法必定能求出最小值,spfa對bellman ford演算法優化的關鍵之處在於意識到 只有那些在前一遍鬆弛中改變了距離估計值的點,才可能引起他們的鄰接點的距離估計值的改變。為什麼隊列為空就不改變了呢?就是因為要到下一點必須經過它的前乙個鄰接點。spfa可以處理負權邊。很...