Johnson全源最短路

2022-05-05 17:15:09 字數 2038 閱讀 7377

例題:p5905 【模板】johnson 全源最短路

首先考慮求全源最短路的幾種方法:

好像……只有dijkstra還有希望?但負權邊處理不了真是很棘手啊。

一種方法是讓每條邊都加上乙個數\(x\)使得邊權為正,但考慮下圖:

\(1\)到\(2\)的最短路應為:\(1 -> 3 -> 4 -> 2\),長度為\(-1\)。如果我們把每條邊的邊權都加上\(5\):

此時的最短路是:\(1 -> 5 -> 2\),就不是實際的最短路了,所以這種方法行不通

注:經本人研究,應該是兩條路徑進過的邊的數量不同而導致的

接下來,就該 johnson 登場啦!johnson 其實就是用另一種方法標記邊權啦。

首先來看看實現方法:我們新建乙個虛擬結點(不妨設他的編號為0),由他向其他的所有結點都連一條邊權為\(0\)的邊,然後求0號節點為源點的單源最短路,存到乙個\(h\)陣列中。然後,讓每條邊的權值\(w\)變為\(w+h_u-h_v\),這裡\(u\)和\(v\)分別為這條邊的起點和終點。然後再以每個點為源點做 dijkstra 就ok了。

q:那這麼說,dijkstra 也可以求出負權圖(無負環)的單源最短路徑了?

a:沒錯。但是預處理要跑一遍 bellman-ford,還不如直接用 bellman-ford 呢。

如何證明這是正確的呢?

首先,從\(s\)到\(t\)的路徑中隨便取出一條:

\[s -> p_1 -> p_2 -> \cdots -> p_k -> t

\]則這條路徑的長度為:

\[(w_+h_s-h_)+(w_+h_-h_)+\dots+(w_+h_-h_t)

\]簡化後得到:

\[w_+w_+\cdots+w_+h_s-h_t

\]可以發現,不管走哪條路徑,最後都是\(+h_s-h_t\),而\(h_s\)和\(h_t\)又是不變的,所以最終得到的最短路徑還是原來的最短路徑。

到這裡已經證明一半了,接下來要證明得到的邊權非負,必須要無負權邊才能使 dijkstra 跑出來的結果正確。根據三角形不等式(就是那個三角形裡任意兩條邊的長度之和大於等於另一條邊的長度),新圖上的任意一條邊\((u,v)\)上的兩點滿足:\(h_v \le w_+h_u\),則新邊的邊權\(w_+h_u-h_v \ge 0\)。所以新圖的邊權非負。

正確性證明就是這個亞子。

**實現(注意處理精度問題,該開ll的時候開ll):

#include#include#define maxn 5005

#define maxm 10005

#define inf 1e9

using namespace std;

int n,m;

int vis[maxn];

long long h[maxn],dis[maxn];

bool f[maxn];

struct graph

}g;//鏈式前向星

bool spfa(int s)//這裡用了bellman-ford的佇列優化

}} return true;

}void dijkstra(int s)

} }return ;

}int main()

for(int i=1;i<=n;i++) g.add(0,i,0);//建虛擬節點0並且往其他的點都連一條邊權為0的邊

if(!spfa(0))//求h的同時也判了負環

for(int u=1;u<=n;u++)

for(int i=g.hd[u];i;i=g.nxt[i])

g.dt[i]+=h[u]-h[g.to[i]];//求新邊的邊權

for(int i=1;i<=n;i++)

return 0;

}

最後安利一發客

johnson全源最短路

一下都假設該有向圖 無向圖同理 有n個點,m條邊。談及全源最短路,第乙個想到的是弗洛伊德演算法,簡單有效,因為並非本篇文章重點,所以只是把 放在這裡 int main 唯一要注意的就是必須要列舉轉折點。時間複雜度o n 3 當n很大時不是乙個可以接受的數字。或者跑n遍單元最短路。你可以用spfa跑,...

Johnson演算法 多源最短路演算法

請不要輕易點選標題 乙個適用於求可含負邊權的稀疏圖的多源最短路演算法 時間複雜度 o n cdot m cdot log m n cdot m 空間複雜度 o n m 該演算法綜合利用了dijkstra演算法和bellman ford演算法 不要慌,雖然有負邊但dijkstra可以跑 在開始講解之前...

最短路 Johnson 演算法

這個演算法可以用於處理稀疏圖,帶有負權的任意兩點間的最短路問題 如果點數 但是dij中不能存在負的邊權,所以我們要考慮一種轉化邊權的方法 可以通過類似差分約束的方法來重構邊權 對一條邊 u,v 他們的權值為e dis i 表示1 i的最短路 我們可以得到dis v dis u e 因此就可以把變成d...