單源最短路問題複習
在學習單源最短路之前,首先要了解陣列和for迴圈
- for迴圈
for迴圈,顧名思義就是迴圈語句
執行以下程式碼
#include<iostream> #include<cstring> using namespace std; int main(){ for(int i=0;i<=10;i++){ cout<<i<<' '; } return 0; }
得到
執行以下程式碼
#include<iostream> #include<cstring> using namespacestd; int main(){ for(int i=0;i<=10;i++){ cout<<i<<endl; for(int j=0;j<=10;j++){ cout<<j<<' '; } cout<<endl; } return 0; }
得到
這兩個例子可以直觀地理解for迴圈的執行方式和效果
- 陣列
對於一維陣列,形式上可以理解為一個數值可變的數列
對於二維陣列,形式上可以理解為一個矩陣
其作用是有序地儲存變數,
例子如下
#include<iostream> #include<cstring> using namespace std; int main(){ int a[10][10];//宣告一個10*10的二維陣列,僅做儲存資料使用,無數學意義 memset(a,127,sizeof(a));//將陣列中的所有元素全賦值為2139062143 for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ cout<<a[i][j]<<' '; } cout<<endl; }//for迴圈輸出 return 0; }
執行後輸出結果
需要注意的是,陣列的下標從0開始計算;
如:a[10]中包含的元素為a[0]~a[9]
但是在處理問題的時候通常忽略掉a[0],從a[1]開始初始化
- 單源最短路問題介紹
對於如圖所示的圖,字母表示節點,也即地點,其中相連的邊表示兩點間的距離
單源最短路問題是指以一個節點為起點,求它到其餘每個點的最短距離
- 圖的儲存方式
在輸入時,第一行為n,m,s三個數字,分別表示該圖的點數和邊數,以及起點;目標是求該起點到其餘各點的最短路徑
之後m行,每行的第一個數字為該邊的起點編號,第二個數字為終點編號,第三個數字為該邊的長度
eg:
我們以陣列dis[]表示起點s到每個點的路徑長度
初始化時令dis[s]=0
dis[i]=2147483647,其中i<=n且i!=s
(dis[s]即s到自身的距離,為0;而開始時,s到其餘各點的距離未知,設為不可到達,用一個很大的數表示即可)
以陣列u[],v[],w[],分別表示邊i的起點,終點,長度(w表示weight,權重)
則可有以下的輸入程式碼
int n,m,s,u[500001],v[500001],w[500001]
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i]>>w[i];
}
for(int i=1;i<=n;i++)
dis[i]=2147483647;
dis[s]=0;
至此,整個圖儲存完畢
- 求起點s到所有點的最短路徑
比較好理解的:若存在邊(u[j],v[j],w[j])使得dis[v[j]]>dis[u[j]]+w[j]
則說明起點s先到u[j]再到v[j]比s直接到v[j]更快
則令dis[v[j]]=dis[u[j]]+w[j],保證最小
要進行上述操作,則只需執行如下程式碼
for(int j=1;j<=m;j++){ if(dis[v[j]]>dis[u[j]]+w[j]){ dis[v[j]]=dis[u[j]]+w[j]; } }
如果只進行一次上述操作,那麼dis[]中儲存的結果只會是和s相鄰的邊(可以在紙上手動執行以下程式碼),其餘邊沒有進行迭代
為了讓除s外的n-1個點都被遍歷到,我們需要進行n-1次上述操作,即
for(int i=1;i<=n-1;i++){//該外層i迴圈在這裡只是個計數作用
for(int j=1;j<=m;j++){
if(dis[v[j]]>dis[u[j]]+w[j]){
dis[v[j]]=dis[u[j]]+w[j];
}
}
}
至此,dis[i]中的數均變為s到i點的最短路徑
最後輸出即可
for(int i=1;i<=n;i++) cout<<dis[i]<<" "; return 0;
優化先不談
總體程式碼如下:
#include<bits/stdc++.h> using namespace std; int u[500001],v[500001],w[500001]; long long dis[500001]; int n,m,s; int main(){ cin>>n>>m>>s; for(int i=1;i<=n;i++) dis[i]=2147483647; dis[s]=0; for(int i=1;i<=m;i++){ cin>>u[i]>>v[i]>>w[i]; } for(int i=1;i<=n-1;i++){ for(int j=1;j<=m;j++){ if(dis[v[j]]>dis[u[j]]+w[j]){ dis[v[j]]=dis[u[j]]+w[j]; } } } for(int i=1;i<=n;i++) cout<<dis[i]<<" "; return 0; }
將上述例子中的數代入計算一下(用手)
得到可以輸出dis陣列是
0 2 4 3
演算法時間複雜度為O(n*m),非常慢,當n*m比較大時計算機無法承受
end
QAQ