136 lines
4.2 KiB
C
136 lines
4.2 KiB
C
/*
|
||
Program: all-pairs-shortest-path-v1.c (Report comments/bugs to chikh@yuntech.edu.tw)
|
||
Function: 實作Floyd-Warshall演算法,以書附的shortestPath.dat檔案作為輸入
|
||
Notes:
|
||
1) Floyd-Warshall演算法為「動態規劃」(dynamic programming)法
|
||
2) 讀取資料所用程式碼取自書附的shortestPath.c但調整函式的原型(prototype)且避用全域變數
|
||
3) 部分參考 https://www.geeksforgeeks.org/floyd-warshall-algorithm-dp-16/
|
||
與 https://www.programiz.com/dsa/floyd-warshall-algorithm
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h> /* for exit() */
|
||
|
||
#define MAX_V 100 /* 最大頂點數 */
|
||
#define Inf 1073741823 /* 此為2^30-1的大數字,為本程式假設的無限大值;可改用limits.h定義的INT_MAX */
|
||
|
||
void init(int A[][MAX_V], int Y[][MAX_V], int *N) /* 讀取輸入資料檔、設定相鄰矩陣,把相關資料結構作初始化 */
|
||
{
|
||
FILE *fptr;
|
||
int i, j;
|
||
int weight;
|
||
|
||
if ((fptr=fopen("shortestPath.dat","r")) == NULL) { /* 開啟資料檔 */
|
||
perror("shortestPath.dat");
|
||
exit(1);
|
||
}
|
||
|
||
fscanf(fptr,"%d",N); /* 讀取圖形節點數 */
|
||
for (i = 1; i <= *N; i++ )
|
||
for (j = 1; j <= *N; j++) {
|
||
A[i][j] = (i==j)? 0: Inf; /* 初始化A[1..N][1..N]相鄰矩陣,所有元素均先預設為∞ */
|
||
Y[i][j] = (i==j)? i: -1;
|
||
}
|
||
|
||
while (fscanf(fptr,"%d %d %d",&i,&j,&weight) != EOF) {
|
||
A[i][j] = weight; /* 讀取連接頂點i與j的邊的權重值 */
|
||
Y[i][j] = i; /* Y[i][j]預設為i */
|
||
}
|
||
|
||
fclose(fptr);
|
||
}
|
||
|
||
/* 底下floydWarshall()函式就每二頂點(i,j)之間計算最短距離值,並記錄可經由哪些中繼頂點達到最短 */
|
||
void floydWarshall(int A[][MAX_V], int Y[][MAX_V], int N)
|
||
{
|
||
int i, j, k;
|
||
|
||
for (k = 1; k <= N; k++)
|
||
for (i = 1; i < N; i++)
|
||
for (j = 1; j <= N; j++)
|
||
if (A[i][k] + A[k][j] < A[i][j]) { //if (A[i][k] != Inf && A[k][j] != Inf && A[i][k] + A[k][j] < A[i][j]) {
|
||
A[i][j] = A[i][k] + A[k][j];
|
||
Y[i][j] = Y[k][j]; /* 不寫為 Y[i][j] = k */
|
||
}
|
||
/* 上面二層for迴圈用於修正每二頂點(i,j)之間的距離值,檢視有否經由k(任一個可能的中繼點)能得到更小距離值 */
|
||
/*
|
||
留意line 53的寫法,不寫為 Y[i][j] = k,同學可區分有何差別嗎?!
|
||
理由:由line 37可知,若二頂點(i,j)之間有邊存在,將把Y[i][j]預設為i。
|
||
因此,在line 53中,若k與j有邊存在,寫為Y[k][j]與寫為k的意義相同,意為到j之前的中繼點為k。
|
||
但若k與j之間無直接的邊連接,則設Y[i][j] = Y[k][j],意為i到j的最後一個中繼點將由從k到j的路徑所記錄的最後
|
||
一個中繼點決定(Y[k][j]紀錄目前所知k到j距離j最近的中繼點)。
|
||
透過line 53設置Y矩陣內容,將可回溯得到最短路徑
|
||
*/
|
||
}
|
||
|
||
void printMatrix(int A[][MAX_V], int N)
|
||
{
|
||
int i, j;
|
||
|
||
printf("\n\n運算後得頂點(i,j)間之矩陣資訊如下:\n ");
|
||
for (i = 1; i <= N; i++) printf("%4d",i);
|
||
for (i = 0; i <= N; i++) printf("%4s",i==0? "\n +":"----");
|
||
|
||
for (i = 1; i <= N; i++) {
|
||
printf("\n頂點%d |",i);
|
||
for (j = 1; j <= N; j++) {
|
||
if (A[i][j] == Inf)
|
||
printf("%5s","∞"); /* somewhat weird to use %5s instead of %4s here */
|
||
else
|
||
printf("%4d",A[i][j]);
|
||
}
|
||
//printf("\n");
|
||
}
|
||
}
|
||
|
||
int backtrack(int A[][MAX_V], int Y[][MAX_V], int source, int dest)
|
||
{
|
||
int distance, k = Y[source][dest]; /* k為source到dest之間的中繼頂點 */
|
||
|
||
if (source == dest) {
|
||
printf("頂點%d →",source);
|
||
return A[source][dest];
|
||
}
|
||
if (Y[source][dest] == -1) {
|
||
printf("頂點%d與%d之間沒有路徑存在:( ",source,dest);
|
||
return Inf;
|
||
}
|
||
distance = backtrack(A,Y,source,k);
|
||
printf(" %d →",dest);
|
||
|
||
return distance+A[k][dest];
|
||
}
|
||
|
||
void traverseMatrix(int A[][MAX_V],int Y[][MAX_V])
|
||
{
|
||
int distance, source, sink;
|
||
|
||
printf("\n\n查詢任二點之間的最短路徑\n源點(0退出查詢) => ");
|
||
scanf("%d",&source);
|
||
if (source == 0) return;
|
||
printf("終點(0退出查詢) => ");
|
||
scanf("%d",&sink);
|
||
if (sink == 0) return;
|
||
|
||
printf("最短路徑:");
|
||
distance = backtrack(A,Y,source,sink);
|
||
printf("\b\b (所經之邊距離總和=%d)(查表A[%d][%d]=%d)",distance,source,sink,A[source][sink]); /* 抹除最後顯示多餘的"→" */
|
||
}
|
||
|
||
int main()
|
||
{
|
||
int N, A[MAX_V][MAX_V], Y[MAX_V][MAX_V] = {0};
|
||
/* N為圖所含的頂點實際總數;A[1..N][1..N]為圖形的相鄰矩陣 */
|
||
/* Y[1..N][1..N]記錄任二頂點(i,j)之間距j最近的中繼點,將用於最短路徑回溯之用 */
|
||
|
||
printf("\n*** 解 all-pairs shortest-path 問題 ***");
|
||
|
||
init(A,Y,&N); /* 讀取輸入資料檔、設定相鄰矩陣,並把相關資料結構作初始化 */
|
||
floydWarshall(A,Y,N);
|
||
printMatrix(A,N);
|
||
//printMatrix(Y,N);
|
||
traverseMatrix(A,Y);
|
||
|
||
return 0;
|
||
}
|