題目連結: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 }