5行程式碼搞定floyd演算法
簡介
floyd是圖搜演算法中很經典的一個演算法,用於求一副圖中任意兩點之間的最短路徑(時間,花費等)。其演算法思想感覺比Dijkstra簡單,而且程式碼也很容易實現。不過就是效率比較低,三個for迴圈導致複雜度為O(n3)。
例項
假如有如下的地圖,圖中四個點代表不同的城市,帶箭頭的邊表示各城市間的航線(城市1可以飛到城市2,但城市2不可直接飛到城市1,只能通過其他城市週轉),航線附近的數字為機票價格。
我們利用二維矩陣儲存這幅地圖的各個城市和每條航線的資訊。
比如1號城市到2號城市的機票為2元,則設graph[1][2]的值為2。2號城市無法到達4號城市,則設定graph[2][4]的值為∞(自己設定一個較大的值即可)。另外此處約定一個城市自己飛到自己的票價為0,例如graph[1][1]為0,具體如下:
那麼如何求任意城市之間最少的機票開銷呢?
其實思想很簡單,就是通過一箇中間城市週轉一下。例如從城市1直接飛到城市3需要6元,但是可以選擇先從城市1飛到城市2(2元),再從城市2飛到城市3(3元),這樣只需要花費2+3=5元即可。
同理,從城市4直接飛到城市3機票需要12元,而可以選擇先從4飛到1(5元),再從1飛到3(6元),花費降到5+6=11元。為了省1塊錢,旅途要波折一點。
如果為了喪心病狂地省錢,不怕麻煩,甚至可以先從城市4飛到城市1,然後從1飛到2,再從2飛到3,這樣只需要5+2+3=10塊錢,比4直飛3的12塊整整省了2元。
演算法思想
所以,如果要讓任意兩個城市(例如從頂點a點到頂點b)之間的開銷變少,只能引入第三個城市(頂點k),並通過這個頂點k中轉即a->k->b,才可能縮短原來從頂點a點到頂點b的開銷。那麼這個中轉的頂點k是1~n中的哪個點呢?甚至有時候不只通過一個點,而是經過兩個點或者更多點中轉開銷則會更少,即a->k1->k2->b或者a->k1->k2…->ki->……->b。
如果現在只允許經過1號城市,求任意兩個城市之間的最低票價,應該如何求呢?只需判斷graph[i][1]+graph[1][j]是否比graph[i][j]要小即可。graph[i][j]表示的是從i號城市到j號城市之間的機票花費。graph[i][1]+graph[1][j]表示的是從i號城市先到1號城市,再從1號城市到j號城市的票價之和。其中i是1~n迴圈,j也是1~n迴圈,程式碼實現如下:
for(i = 1;i <= n;i++) {
for(j = 1;j <= n;j++) {
if ( graph[i][j] > graph[i ][1] + graph[1][j] )
graph[i][j] = graph[i][1] + graph[1][j];
}
}
在只允許通過1號城市週轉的情況下,任意兩個城市之間的最低機票花費更新為:
由上圖可知:在只通過1號城市中轉的情況下,3號城市到2號城市(graph[3][2])、4號城市到2號城市(graph[4][2])以及4號城市到3號城市(graph[4][3])的票價開銷都變少了。
接下來繼續求在只允許經過1和2號兩個城市中轉的情況下任意兩點之間的最低票價。此時只需要在經由1號城市週轉的結果之下,加入2號城市,再判斷如果經過2號城市是否可以使得i號城市到j號城市之間的票價變得更少。即判斷graph[i][2]+graph[2][j]是否比graph[i][j]要小,程式碼實現為如下:
//經過1號頂點
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if (graph[i][j] > graph[i][1]+graph[1][j])
graph[i][j]=graph[i][1]+graph[1][j];
//經過2號頂點
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if (graph[i][j] > graph[i][2]+e[2][j])
graph[i][j]=graph[i][2]+graph[2][j];
所以依次類推,只需要在最外層迴圈中,不斷的增加可以中轉的城市即可:
//多源最短路徑 floyd演算法(c/c++)
void floyd(int graph[][5],int points_num) {
for(int k=1;k<=points_num;k++)
for(int i=1;i<=points_num;i++)
for(int j=1;j<=points_num;j++)
if(graph[i][j]>graph[i][k]+graph[k][j])
graph[i][j]=graph[i][k]+graph[k][j];
}
完整測試程式碼
注意矩陣從(1,1)元素開始
演算法定義
define INF 999999 //定義無窮大值
#define POINTS 100 //定義城市數(頂點數)
#define EDGES 100 //定義航線數(路徑數)
//建立圖
void createGraph(int graph[][POINTS]) {
int mid_tmp,
points_num,
edges_num;
//獲取點數 & 邊數
cout<<"請輸城市數量,航線數量"<<endl;
scanf("%d %d",&points_num,&edges_num);
//全部初始化為正無窮
for (int i = 1;i <= points_num;++i)
for (int j = 1;j <= points_num;++j) {
if (i == j)
graph[i][j] = 0;
else
graph[i][j] = INF;
}
cout<<"請輸入各城市間的航線 ,票價"<<endl;
//輸入邊
int m,n,length;
for (int i = 1;i <= edges_num;++i) {
scanf("%d %d %d",&m,&n,&length);
graph[m][n] = length;
}
}
//多源最短路徑 floyd演算法
void floyd(int graph[][POINTS],int points_num) {
for(int k=1;k<=points_num;k++)
for(int i=1;i<=points_num;i++)
for(int j=1;j<=points_num;j++)
if(graph[i][j]>graph[i][k]+graph[k][j])
graph[i][j]=graph[i][k]+graph[k][j];
}
//輸出最終的結果
void showGraph(int graph[][POINTS],int n) {
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) {
printf("%10d",graph[i][j]);
}
printf("\n");
}
}
主函式
int main(void) {
int graph[POINTS][POINTS];
createGraph(graph);
floyd(graph,4);
cout<<"任意城市最低票價圖:"<<endl;
showGraph(graph,4);
system("pause");
return 0;
}