最短路徑問題 動態規劃
問題參考:
現有一張地圖,各結點代表城市,兩結點間連線代表道路,線上數字表示城市間的距離。如圖1所示,試找出從結點A到結點E的最短距離。
我們可以用深度優先搜尋法來解決此問題,該問題的遞迴式為
其中是與v相鄰的節點的集合,w(v,u)表示從v到u的邊的長度。
這個程式的效率如何呢?我們可以看到,每次除了已經訪問過的城市外,其他城市都要訪問,所以時間複雜度為O(n!),這是一個“指數級”的演算法。
首先,我們來觀察一下這個演算法。在求從B1到E的最短距離的時候,先求出從C2到E的最短距離;而在求從B2到E的最短距離的時候,又求了一遍從C2到E的最短距離。也就是說,從C2到E的最短距離我們求了兩遍。同樣可以發現,在求從C1、C2到E的最短距離的過程中,從D1到E的最短距離也被求了兩遍。而在整個程式中,從D1到E的最短距離被求了四遍。如果在求解的過程中,同時將求得的最短距離"記錄在案",隨時呼叫,就可以避免這種情況。於是,可以改進該演算法,將每次求出的從v到E的最短距離記錄下來,在演算法中遞迴地求MinDistance(v)時先檢查以前是否已經求過了MinDistance(v),如果求過了則不用重新求一遍,只要查詢以前的記錄就可以了。這樣,由於所有的點有n個,因此不同的狀態數目有n個,該演算法的數量級為O(n)。
更進一步,可以將這種遞迴改為遞推,這樣可以減少遞迴呼叫的開銷。
程式碼如下:
#include <iostream> #include <fstream> using namespace std; ifstream fin("in.txt"); #define maxLength 20 int matrix[maxLength][maxLength]; //有向圖的鄰接表 int minPath[maxLength]; //儲存這每個節點到終點的最短路徑 int trace[maxLength]; //記錄下最短線路 int v_n; //節點個數 int MinDistance(int v) { if(minPath[v]>0) return minPath[v]; if(v==v_n-1) return 0; //邊界值 int min=1000,t,j; for(int i=v+1;i<v_n;i++) { if(matrix[v][i]>0) { t = matrix[v][i]+MinDistance(i); if(min>t){ min=t; j=i;} } } minPath[v]=min; trace[v]=j; return minPath[v]; } int main() { fin>>v_n; for(int i=0;i<v_n;i++) { for(int j=0;j<v_n;j++) { fin>>matrix[i][j]; cout<<matrix[i][j]<<"-"; } cout<<endl; } memset(minPath,0,sizeof(int)*maxLength); memset(trace,0,sizeof(int)*maxLength); int minD = MinDistance(0); cout<<"最短路徑:"<<minD<<endl; i=0; cout<<"1-->"; while(minD>0) { cout<<trace[i]+1<<"-->"; minD = minD-matrix[i][trace[i]]; i = trace[i]; } cout<<endl; return 0; }
輸入檔案:in.txt
(例子來源於《演算法設計與分析》(夏紅霞 主編)教材147頁例題)
12
0 9 7 3 2 0 0 0 0 0 0 0
0 0 0 0 0 4 2 1 0 0 0 0
0 0 0 0 0 2 7 0 0 0 0 0
0 0 0 0 0 0 0 11 0 0 0 0
0 0 0 0 0 0 11 8 0 0 0 0
0 0 0 0 0 0 0 0 6 5 0 0
0 0 0 0 0 0 0 0 5 3 0 0
0 0 0 0 0 0 0 0 0 5 6 0
0 0 0 0 0 0 0 0 0 0 0 4
0 0 0 0 0 0 0 0 0 0 0 2
0 0 0 0 0 0 0 0 0 0 0 5
0 0 0 0 0 0 0 0 0 0 0 0
輸出檔案:
0-9-7-3-2-0-0-0-0-0-0-0-
0-0-0-0-0-4-2-1-0-0-0-0-
0-0-0-0-0-2-7-0-0-0-0-0-
0-0-0-0-0-0-0-11-0-0-0-0-
0-0-0-0-0-0-11-8-0-0-0-0-
0-0-0-0-0-0-0-0-6-5-0-0-
0-0-0-0-0-0-0-0-5-3-0-0-
0-0-0-0-0-0-0-0-0-5-6-0-
0-0-0-0-0-0-0-0-0-0-0-4-
0-0-0-0-0-0-0-0-0-0-0-2-
0-0-0-0-0-0-0-0-0-0-0-5-
0-0-0-0-0-0-0-0-0-0-0-0-
最短路徑:16
1-->2-->7-->10-->12-->
Press any key to continue