SPFA 演算法(剪輯)(學習!)

2022-04-28 23:45:23 字數 3456 閱讀 9383

spfa演算法

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

思路:

簡潔起見,我們約定有向加權圖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,則有負權環

均次數為k,有辦法證明對於通常的情況,k在2左右。 

**:

存圖的方式我是利用鏈式向前星的方式存圖的,下面是**:

#include

#include

#include

#define maxn 100

#define maxm 10000

#define max 10000

int used[maxn],outqueue[maxn],head[maxn],queue[maxn],low[maxn],n,m;

struct edge

edge[maxm];

bool spfa(int start)

}i++;

}return true;

}int main()

if (spfa(1))

printf

("%d\n", low[n]);

else

printf

("不存在最短\n");}}

剛才那個**是最短路裡面效率最高的,為了提高效率,佇列那個是自己寫的,而不是呼叫stl裡面的,但是看起來比較難,很難讀懂,下面是用stl裡面的queue寫的。

完整**:

// spfa演算法

#include #include #include #include #include #define maxn 100

#define maxm 10000

#define max 10000

using namespace std;

int used[maxn],outqueue[maxn];

int head[maxn],low[maxn];

int n,m;

struct edge

edge[maxm];

bool spfa (int start)}}

return true;

}int main()

if (spfa(1))

printf ("%d\n", low[n]);

else

printf ("不存在最短\n");

}return 0;

}

Kuhn Munkres演算法 剪輯 (備用)

km演算法是通過給每個頂點乙個標號 叫做頂標 來把求最大權匹配的問題轉化為求完備匹配的問題的。設頂點xi的頂標為a i 頂點yi的頂標為b i 頂點xi與yj之間的邊權為w i,j 在演算法執行過程中的任一時刻,對於任一條邊 i,j a i b j w i,j 始終成立。km演算法的正確性基於以下定...

Bellman Ford演算法,SPFA演算法

bellman ford 演算法能在更普遍的情況下 存在負權邊 解決單源點最短路徑問題。對於給定的帶權 有向或無向 圖g v,e 其源點為 s,加權函式w是 邊集e 的對映。對圖g執行 bellman ford 演算法的結果是乙個布林值,表明圖中是否存在著乙個從源點s 可達的負權迴路。若不存在這樣的...

Spfa演算法模版

const maxn 5000 type link node node record x,dis longint next link end var g array 1.maxn of link dist,q array 1.maxn of longint v array 1.maxn of boo...