0016:單源最短路徑(dijkstra演演算法)

2022-07-09 12:00:21

題目連結:https://www.luogu.com.cn/problem/P4779

題目描述:給定一個 n 個點,m 條有向邊的帶非負權圖,計算從 s 出發,到每個點的距離。

 

 

 

 

這道題就是一個單源最短路徑的模板,有兩種做法:

1.Floyd演演算法

暴力列舉出所有起點、終點以及中間值,然後算出每兩個點間的最小值。

但這個演演算法時間複雜度較高,是O(n^3),很容易爆掉,在這道題甚至拿不到分。

程式碼:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int arr[10000][10000];
 4 int main(){
 5     int n,m,s,i,j,k,a,b,c;
 6     cin>>n>>m>>s;
 7     for(i=1;i<=n;i++){
 8         for(j=1;j<=n;j++){
 9             if(i==j){
10                 arr[i][j]=0;
11             }else{
12                 arr[i][j]=99999999;
13             }
14         }
15     }
16     while(m--){
17         cin>>a>>b>>c;
18         arr[a][b]=min(c,arr[a][b]);
19     }
20     for(k=1;k<=n;k++){
21         for(i=1;i<=n;i++){
22             if(i==k||arr[i][k]==99999999){
23                 continue;
24             }
25             for(j=1;j<=n;j++){
26                 arr[i][j]=min(arr[i][j],arr[i][k]+arr[k][j]);
27             }    
28         }
29     }
30     for(i=1;i<=n;i++){
31         cout<<arr[s][i]<<" ";    
32     }
33 }

 

 這道題我們要用一種更高階的演演算法——

2.dijkstra演演算法

在無負權邊的情況下,時間複雜度為 O(n log n)基本可以順利通過所有模板題。

先確定初始點到其他所有點的路徑(可能為無窮),然後從和該點距離最小點開始遍歷,不斷更新這些點與初始點的最小距離(學術名叫鬆弛),最後求出初始點與所有其他點的最短路。

然後要通過此題,還需要前向星存邊和優先佇列(堆)優化,可能比較難理解,自己畫圖模擬即可。

上程式碼(有註釋):

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 long long vis[100001]={0},head[100001],dis[100001],cnt,n,m,s,a,b,c;
 4 long long INF=2147483647;//2的31次方,可以看做無窮 
 5 struct Q{
 6     int a,b,c,next;
 7 };//鄰接表,在有向圖中儲存起點、終點權值,next用來前向星存邊 
 8 struct node{//放進優先佇列中的結構體 
 9     int w,now;//w為最短路,now為點 
10     bool operator <(const node &x)const{
11         return w>x.w;//權值從大到小排 
12     }
13 };
14 priority_queue<node> q;//優先佇列 
15 Q e[500001];
16 void add(int a,int b,int c){//前向星存邊 
17     e[cnt++].a=a;
18     e[cnt].b=b;
19     e[cnt].c=c;
20     e[cnt].next=head[a];//next儲存上一個cnt值,方便for迴圈從後往前遍歷邊 
21     head[a]=cnt;
22 } 
23 void dijkstra(){
24     for(int i=1;i<=n;i++){
25         dis[i]=INF;
26     }
27     dis[s]=0;
28     q.push((node){0,s});//將起點壓入佇列 
29     while(!q.empty()){//佇列非空 
30         node x=q.top();//彈出堆頂(最小)元素 
31         q.pop();
32         int u=x.now;
33         if(vis[u]==1){
34             continue;//遍歷完無需再遍歷 
35         }
36         vis[u]=1;
37         for(int i=head[u];i;i=e[i].next){//用前向星遍歷 
38             int v=e[i].b; 
39             dis[v]=min(dis[v],dis[u]+e[i].c);//鬆弛操作 
40             q.push((node){dis[v],v});
41         }
42     }
43 }
44 int main(){
45     cin>>n>>m>>s;
46     for(int i=0;i<m;i++){
47         cin>>a>>b>>c;
48         add(a,b,c);
49     }
50     dijkstra(); 
51     for(int i=1;i<=n;i++){
52         cout<<dis[i]<<" ";
53     }
54 }