1. 程式人生 > 其它 >單源最短路問題複習

單源最短路問題複習

在學習單源最短路之前,首先要了解陣列和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 namespace
std; 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