1. 程式人生 > >Floyd 演算法思想

Floyd 演算法思想

今天總結的是圖的最短路徑的另外一種演算法---弗洛伊德演算法。與前面迪傑斯特拉演算法不同的是,弗洛伊德演算法求的是圖中任意一對頂點之間的最短路徑,當然,仍然針對有向帶權圖。

  我們就先直接進入演算法的演算過程吧~大家可以在這個演算過程中,體會到弗洛伊德演算法是如何表示任意兩點間的最短路徑的。

  演算法準備:

邊權值非負

可以處理環的問題

  1、圖的鄰接矩陣(與以往不同的是,主對角線,也就是自身到自身,這次我們不使用無窮大表示,而是採用0。書上和習題中是這樣的,我們就入鄉隨俗吧~)

  2、二維陣列A,用於存放任意一對頂點之間的最短路徑權值。

  3、二維陣列Path,用於存放任意一對頂點之間的最短路徑。每個單元格的內容表示從i點到j點途經的頂點。

演算法過程:

  1、初始化過程:將鄰接矩陣copy到二維陣列A中,將二維陣列Path中所有元素填充為-1(都沒有開始尋找,哪裡來的中間頂點呢)。、

2、列出頂點的所有可能二元組,自己到自己不算。這裡為

 {0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2}

  3、選擇編號為0的點為中間點,從【2】中二元組集合的第一個元素開始,執行以下過程

       3.1:用i,j兩個變數分別指向二元組裡的兩個元素,比如{0,1}這個二元組,i指向0;j指向1

       3.2:判斷A[i][j] > A[i][0] + A[0][j]嗎?如果表示式為真,進入3.3;若為假,則結束本次過程,進入下一個二元組

       3.3:更新A[i][j]的值為A[i][0] + A[0][j],Path[i][j]的值為0

分析:步驟3其實挺形象的,也是演算法核心。二維陣列A就是圖鄰接矩陣的copy,我們以行的角度來看這個矩陣,比如A[0][1] = 5,表示從頂點0到頂點1有一條邊相連,權值為5。而在這次迴圈中,我們選擇了0為中間點,把步驟3.2的表示式翻譯過來就是說:從i點到j點的代價會大於從i點到0點,再從0點到j點的代價嗎?是不是非常形象地把頂點0作為中間點這個思想表示出來了呢?如果滿足3.2中的表示式,就表示我經過中間點0到目的地比我直接去目的地的代價還要來的少,於是我們更新A[i][j]的值為新求出的這個比原來小的代價的值,Path[i][j]的值更新為0,就表示從i點到j點,要經過0點。非常巧妙地記錄了路徑。(PS.這怎麼有點向我以前看過的深度優先搜尋走迷宮村路徑的方式呀~,這個與數學中的定積分的某一條性質也特別像。)

   4、重複步驟【3】,直到所有的頂點都做過一次中間點為止。

最後兩個矩陣的值如下:

下面一個步驟就是找出兩點間的路徑了,比如頂點1到頂點0,我們看陣列Path

Path[1][0] = 3,說明頂點3是途徑頂點

Path[3][0] = 2,說明頂點2是途徑頂點

Path[2][0] = -1,說明頂點2到頂點0沒有途徑頂點,也就是說,可以由頂點2直接到頂點0,即它們有邊連線。

所以,序列為1->3->2->0,顯然,這是一個逐層遞進,遞迴的過程。
 

#include<cstdio>
using namespace std;
#define INF 1e9
const int maxn=100+10;
int n,m;//點數,邊數,點從0到n-1編號
int dist[maxn][maxn];//記錄距離矩陣
int path[maxn][maxn];//path[i][j]=x表示i到j的路徑上(除i外)的第一個點是x.
void init()
{
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    {
        dist[i][j] = i==j?0:INF;//其實這裡d[i][j]應該還要通過輸入讀資料的
        path[i][j]=j;
    }
 
    //讀取其他dist[i][j]的值
}
void floyd()
{
    for(int k=0;k<n;k++)
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    if(dist[i][k]<INF && dist[k][j]<INF )
    {
        if(dist[i][j]>dist[i][k]+dist[k][j])
        {
            dist[i][j] = dist[i][k]+dist[k][j];
            path[i][j] = path[i][k];
        }
        else if(dist[i][j] == dist[i][k]+dist[k][j] &&path[i][j]>path[i][k])
        {
            path[i][j] = path[i][k];  //最終path中存的是字典序最小的路徑
        }
    }
}
 
int main()
{
    //讀n和m
    init();
    //讀m條邊
    floyd();
    //輸出所求最短路徑距離
    return 0;
}