Data_Structure/作業/unit12/all-pairs-shortest-path-v1.c
2025-01-20 21:25:33 +08:00

136 lines
4.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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;
}