前言
歡迎來到CSP考前復習系列。。。。。。今天要講的是Dijkstra。。。
當然,如果有任何錯誤的話,歡迎留言指出喲。。。
算法作用
Dijkstra算法用於解決單源最短路問題,即求取從一個給定的起點出發到其他節點的最短距離。
算法原理
我們首先定義一個數組$dis$,代表我們選定的起點到其他各個點的距離最小值。
然后,將$dis$數組中除了起點以外的所有的元素都賦成$inf$(無限大)。
然后開始掃描起點所連接的點,找出一個直接距離最短的點,加入已生成的樹中,並將連接它們的這條邊加入最小生成樹中。
然后繼續,從已有的最小生成樹中的所有點出發,找到一個距離最近的,繼續加入生成樹。
算法結果
算法運行結束后,將會得到一個處理好的數組$dis$,其中$dis[i]$代表從起點出發到節點$i$的最短路長度。
算法實現
朴素方法
這個普通的寫法我並不想過多介紹,因為這樣做太過於普通,效率非常低。
你可以使用鄰接矩陣來存儲整個圖,然后每次枚舉對應的行或列來找到一個距離最近的。
代碼也比較簡單,這里並不想過多描述。事實上,我一開始就寫的堆優化,因此再把它改成朴素算法將會比較多余。
堆優化
堆優化的主要思想就是使用一個優先隊列(就是每次彈出的元素一定是整個隊列中最小的元素)來代替最近距離的查找,用鄰接表代替鄰接矩陣,這樣可以大幅度節約時間開銷。
在這里有幾個細節需要處理:
首先來講,優先隊列的數據類型應該是怎樣的呢?
我們知道優先隊列應該用於快速尋找距離最近的點。由於優先隊列只是將最小的那個元素排在前面,因此我們應該定義一種數據類型,使得它包含該節點的編號以及該節點當前與起點的距離。
我們應該在什么時候對隊列進行操作呢?
隊列操作的地方,首先就是搜索剛開始,要為起點賦初始值,此時必須將起點加入優先隊列中。該隊列元素的節點編號為起點的編號,該節點當前與起點的距離為00。
那么如果一個節點到起點的最短距離通過其他的運算流程發生了變化,那么如何處理隊列中的那個已經存入的元素?
事實上,你不需要理會隊列中的元素,而是再存入一個就行了。因為如果要發生變化,只能將節點與起點之間的距離變得更小,而優先隊列恰好是先讓最小的那個彈出。
因此,輪到某一個隊列元素彈出的時候,如果有多個元素的節點編號相同,那么被彈出的一定是節點編號最小的一個。等到后面再遇到這個節點編號的時候,我們只需要將它忽略掉就行了。
注意:$dijkstra$不能跑有負環的圖和最長路(最長路的貪心是錯的,自己手玩一下就好)
1 #include <cstdio> 2 #include <queue> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 int n,m,s; 7 int head[100001],dis[100001],cnt; 8 bool vis[100001]; 9 struct qwq{ 10 int from,to,next; 11 long long len; 12 }edge[200001]; 13 void add(int u,int v,int w) 14 { 15 cnt++; 16 edge[cnt].from=u; 17 edge[cnt].to=v; 18 edge[cnt].len=w; 19 edge[cnt].next=head[u]; 20 head[u]=cnt; 21 return; 22 } 23 struct node{ 24 int index,dist; 25 bool operator < (const node &x)const 26 { 27 return dist>x.dist; 28 } 29 }; 30 priority_queue<node> q; 31 int main() 32 { 33 scanf("%d%d%d",&n,&m,&s); 34 for(int i=1;i<=m;i++) 35 { 36 int u,v,w; 37 scanf("%d%d%d",&u,&v,&w); 38 add(u,v,w); 39 } 40 for(int i=1;i<=n;i++) 41 dis[i]=2147483647; 42 dis[s]=0; 43 q.push(node{s,0}); 44 while(!q.empty()) 45 { 46 node x=q.top(); 47 q.pop(); 48 int u=x.index; 49 if(vis[u]) continue; 50 vis[u]=1; 51 for(int i=head[u];i;i=edge[i].next) 52 { 53 if(dis[edge[i].to]>dis[u]+edge[i].len) 54 { 55 dis[edge[i].to]=dis[u]+edge[i].len; 56 q.push(node{edge[i].to,dis[edge[i].to]}); 57 } 58 } 59 } 60 for(int i=1;i<=n;i++) 61 printf("%d ",dis[i]); 62 return 0; 63 }
