Dijkstra再理解 最短路計數

2022-05-05 18:48:10 字數 3636 閱讀 2453

眾所周知,dijkstra演算法是跑單源最短路的一種優秀演算法,不過他的缺點在於難以處理負權邊。

但是由於在今年的noi賽場上spfa那啥了(嗯就是那啥了),所以我們還是好好研究一下dij的原理和它的優化吧。

(前面那篇寫的太簡陋了)

1.dijkstra演算法的原理

首先,我們先假設整個圖已經被建完而且所有邊權全部為正。使用dis[i]表示從原點s到i點的最短距離。之後我們從選取的原點s開始,進行到匯點t的最短路搜尋。s一開始就會連向幾個頂點,它所能到達的頂點的距離我們更新一

下,而不能到達的頂點的距離就先設為inf。之後,我們在這個點所能到達的所有點之中,找出乙個dis最小的點,那麼此時,到這個點的最短路徑就已經被確定了。

這是為什麼呢?因為圖中所有邊權全部為正,而當前點(u)的dis已經是最小的了,從其他點(v)再到這個點,所經過的距離必然大於dis[u],也就是肯定不是到達u點的最短路。

因此這樣我們就可以不斷地確定一些點,之後再從確定的點集出發,去確定更多的點,這樣就可以找到到匯點t的最短路徑長度了。

(我偷了別人的圖和文章來演示如何求從頂點v1到其他各個頂點的最短路徑)

首先第一步,我們先宣告乙個dis陣列,該陣列初始化的值為: 

我們的頂點集t的初始化為:t=

既然是求 v1頂點到其餘各個頂點的最短路程,那就先找乙個離 1 號頂點最近的頂點。通過陣列 dis 可知當前離v1頂點最近是 v3頂點。當選擇了 2 號頂點後,dis[2](下標從0開始)的值就已經從「估計值」變為了「確定值」,即 v1頂點到 v3頂點的最短路程就是當前 dis[2]值。將v3加入到t中。 

為什麼呢?因為目前離 v1頂點最近的是 v3頂點,並且這個圖所有的邊都是正數,那麼肯定不可能通過第三個頂點中轉,使得 v1頂點到 v3頂點的路程進一步縮短了。因為 v1頂點到其它頂點的路程肯定沒有 v1到 v3頂點短.

ok,既然確定了乙個頂點的最短路徑,下面我們就要根據這個新入的頂點v3會有出度,發現以v3 為弧尾的有: < v3,v4 >,那麼我們看看路徑:v1–v3–v4的長度是否比v1–v4短,其實這個已經是很明顯的了,因為dis[3]代表的就是v1–v4的長度為無窮大,而v1–v3–v4的長度為:10+50=60,所以更新dis[3]的值,得到如下結果: 

因此 dis[3]要更新為 60。這個過程有個專業術語叫做「鬆弛」。即 v1頂點到 v4頂點的路程即 dis[3],通過 < v3,v4> 這條邊鬆弛成功。這便是 dijkstra 演算法的主要思想:通過「邊」來鬆弛v1頂點到其餘各個頂點的路程。

然後,我們又從除dis[2]和dis[0]外的其他值中尋找最小值,發現dis[4]的值最小,通過之前是解釋的原理,可以知道v1到v5的最短距離就是dis[4]的值,然後,我們把v5加入到集合t中,然後,考慮v5的出度是否會影響我們的陣列dis的值,v5有兩條出度:< v5,v4>和 < v5,v6>,然後我們發現:v1–v5–v4的長度為:50,而dis[3]的值為60,所以我們要更新dis[3]的值.另外,v1-v5-v6的長度為:90,而dis[5]為100,所以我們需要更新dis[5]的值。更新後的dis陣列如下圖: 

然後,繼續從dis中選擇未確定的頂點的值中選擇乙個最小的值,發現dis[3]的值是最小的,所以把v4加入到集合t中,此時集合t=,然後,考慮v4的出度是否會影響我們的陣列dis的值,v4有一條出度:< v4,v6>,然後我們發現:v1–v5–v4–v6的長度為:60,而dis[5]的值為90,所以我們要更新dis[5]的值,更新後的dis陣列如下圖: 

然後,我們使用同樣原理,分別確定了v6和v2的最短路徑,最後dis的陣列的值如下: 

那麼原理我們就說完了。

2.dijkstra堆優化

dijkstra的功能雖然強大,不過其時間複雜度比較大,對於n個點,每次最壞要列舉n次,時間複雜度是o(n^2)的。

這個複雜度有點大,我們考慮如何來優化呢?

首先,dijkstra是基於貪心的,他每次都是找當前dis值最小的那乙個點來繼續更新。這樣的話,每次的列舉就顯得無用,我們直接維護當前最小值就可以了!所以優化就是使用set(小根堆)維護最小值,之後每次在貪心的時候取堆首元素,再更新堆首元素所能走到的點。每次更新的時候,把原來點儲存的資訊從set裡面刪掉,再壓乙個新的進去就可以了。時間複雜度被優化為o(nlogn)。

**看下面的例題吧。

3.最短路計數

洛谷上的這道題比較簡單,因為是無權圖(其實有權也一樣)。

非常easy的操作,正常跑一遍dijkstra的堆優化,在每次鬆弛操作的時候,如果dis[u] > dis[v] + 1,那麼到達u的最短路個數應該和到達v是一樣的。如果dis[u] = dis[v] + 1,那麼到達v的最短路個數應該加上到達u的最短路個數。

因為所有邊的邊權都是1,所以如果dis[u] = dis[v]+1,那麼肯定從u經過的最短路,在v上經過的時候也是最短路。

這樣就可以做了,注意取個模。

#include#include

#include

#include

#include

#include

#include

#include

#include

#define rep(i,a,n) for(int i = a;i <= n;i++)

#define per(i,n,a) for(int i = n;i >= a;i--)

#define enter putchar('\n')

using

namespace

std;

typedef

long

long

ll;const

int m = 1000005

;const

int mod = 100003

;int

read()

while(ch >= '

0' && ch <= '9'

)

return ans *op;

}#define pr pair#define mp make_pair

setq;

set:: iterator it;

intn,m,ecnt,head[m],x,y,dis[m],num[m];

bool

vis[m];

struct

node

e[m<<1

];void add(int x,int

y)void dij(int

s)

else

if(dis[e[i].to] == dis[k.second] + 1

) }}

}int

main()

如何理解Dijkstra演算法(最短路徑)

假設 n v,是連通網,v是頂點集合,共有n個頂點 對 最短路徑 的兩個理解 case 1,頂點v0到其它所有頂點的最短路徑。case 2,頂點v0到頂點vi的最短路徑。對於dijkstra演算法來說,一次性求出v0到其它所有頂點的最短路徑的時間複雜度是o n 2 dijkstra演算法採用貪心思想...

Dijkstra 最短路徑

dijkstra 最短路徑 針對有向圖,不支援負權值 圖的相鄰矩陣表示方法,還要用到最小值堆 include include define unvisited 0 define visited 1 define infinite 9999 設定最大值 define n 5 定義圖的頂點數 using...

dijkstra最短路徑

hehe和xixi在乙個地方玩遊戲,xixi把n 1件禮物 hehe以前送給xixi的 分別藏在了另外n 1個地方,這些地方都能互相到達,且所有的邊都是有方向的。現在hehe要做的事就是去那些地方找回那n 1件禮物給xixi 由於每一件禮物都有特殊的意義,所以xixi要求hehe每找到一件禮物,就必...