演算法7-15:迪傑斯特拉最短路徑演算法(c語言)
阿新 • • 發佈:2018-12-04
題目描述
在帶權有向圖G中,給定一個源點v,求從v到G中的其餘各頂點的最短路徑問題,叫做單源點的最短路徑問題。
在常用的單源點最短路徑演算法中,迪傑斯特拉演算法是最為常用的一種,是一種按照路徑長度遞增的次序產生最短路徑的演算法。
可將迪傑斯特拉演算法描述如下:
在本題中,讀入一個有向圖的帶權鄰接矩陣(即陣列表示),建立有向圖並按照以上描述中的演算法求出源點至每一個其它頂點的最短路徑長度。
輸入描述
輸入的第一行包含2個正整數n和s,表示圖中共有n個頂點,且源點為s。其中n不超過50,s小於n。
以後的n行中每行有n個用空格隔開的整數。對於第i行的第j個整數,如果大於0,則表示第i個頂點有指向第j個頂點的有向邊,且權值為對應的整數值;如果這個整數為0,則表示沒有i指向j的有向邊。當i和j相等的時候,保證對應的整數為0。
輸出描述
只有一行,共有n-1個整數,表示源點至其它每一個頂點的最短路徑長度。如果不存在從源點至相應頂點的路徑,輸出-1。
請注意行尾輸出換行。
輸入樣例
4 1
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
輸出樣例
6 4 7
提示
在本題中,需要按照題目描述中的演算法完成迪傑斯特拉演算法,並在計算最短路徑的過程中將每個頂點是否可達記錄下來,直到求出每個可達頂點的最短路徑之後,演算法才能夠結束。
迪傑斯特拉演算法的特點是按照路徑長度遞增的順序,依次新增下一條長度最短的邊,從而不斷構造出相應頂點的最短路徑。
另外需要注意的是,在本題中為了更方便的表示頂點間的不可達狀態,可以使用一個十分大的值作為標記。
#include<stdio.h> #include<stdlib.h> #include<string.h> int a[55][55]; //儲存有向圖 int b[55]; //記錄該點是否已經到達 int c[55]; //記錄源點到該點的最短路徑 int n, s; #define intf 0x3f3f3f3f void Dijkstra(int x) { int i, j; b[x] = 1; //標記已達的點 int min = intf; j; for(i = 0; i < n; i++) { if(a[x][i] != 0) //如果不為零才表示該兩點間存在路徑可以進行訪問 { if(c[i] == intf && x == s) //如果是源點則直接更新路徑 { c[i] = a[x][i]; } else if((a[x][i]+c[x]) < c[i] && x != i) //如果從該點到達的新路徑比原陣列中的路徑短,則更新最短路徑 { c[i] = a[x][i]+c[x]; } if(c[i] < min && b[i] == 0) //找到下一條未被訪問的最短路徑 { min = c[i]; j = i; } } // printf("%d %d %d %d\n", x, i, c[i], b[i]); } if(min != intf) Dijkstra(j); } int main() { int i, j; while(~scanf("%d %d", &n, &s)) { for(i = 0; i < n; i++) //對原始資料進行初始化,不可達的兩點間直接將路徑設定為極大值 { for(j = 0; j < n; j++) { scanf("%d", &a[i][j]); if(a[i][j]==0&&i!=j) a[i][j]=intf; } } //for(i=0;i<n;i++) // c[i]=a[s][i]; for(i=0;i<n;i++) //將所有的點標記設為未到達 b[i]=0; Dijkstra(s); for(i = 0; i < n; i++) { if(i != s) { if(c[i] == intf) printf("-1 "); else printf("%d ", c[i]); } } printf("\n"); } return 0; }