1. 程式人生 > >說說弗洛伊德演算法

說說弗洛伊德演算法

弗洛伊德演算法,是當時集訓的時候學最短路問題的第一個演算法(去年寒假),當時學長直說這個演算法好寫、時間複雜度高,至於這個演算法是什麼原理卻沒有講。學長說,照著敲就可以,迴圈敲對了,就那麼神奇的對了,迴圈順序錯了,就神奇的WA了。

今年寒假,我也是學長了,逗比碩把最短路給我了,就變成這一週我要講搜尋和最短路。

搜尋有BFS和DFS兩個演算法,或者說是思想,而最短路有4個演算法。開始重點看搜尋和最短路,翻來覆去好幾遍。然後對搜尋和走最短路的各個演算法都有了進一步的理解,對這幾種東西不再僅僅是限於會用模板了。

在與學弟的交流並解決學弟的疑問的同時,也讓我看到的搜尋和最短路那些只抄模板很容易忽略的地方。其實和學弟交流也能讓自己發現並學到之前自己沒有注意到的東西。

好了,主要說弗洛伊德的思想。之前最短路的總結就寫過弗洛伊德。但沒有解釋弗洛伊德,這篇文章主要解釋弗洛伊德原理。

擺出弗洛伊德的程式碼:

int fld (int n,int s,int e)
{
    int d[200][200];
    int i,j,k;
    memcpy(d,G,sizeof (G));

    for (k = 1;k <= n;k++)
        for (i = 1;i <= n;i++)
            for (j = 1;j <= n;j++)
                d[i][j] = qmin (d[i][j],d[i][k] + d[k][j]);

    return d[s][e];
}

首先,要明確,弗洛伊德是求任意兩點之間的最短路。而他記錄任意兩點之間的距離是靠一個二維陣列實現的。

然後說弗洛伊德是怎麼實現的。

開始的時候,有個d陣列(見上方程式碼),開始的時候是複製的G(一個鄰接矩陣),我們可以認為,這個d開始的時候是兩個點之間相連的距離。

換句話說,就是如果兩個點有相連的邊,就有距離,否則就是正無窮。

然後進入迴圈,從k = 1開始,也就是從1號點開始,對於任意兩個點做一個嘗試:能不能把1號點放入他們中間。

那麼d陣列就不是之前的d陣列了,現在的d陣列可以理解為任意兩點之間中間點不超過1個的最短路。

為什麼是不超過一個呢?因為剛才把1號點嘗試放入任意兩個點中間,有的發現如果放入就會更新最短路,換句話說,如果A到B的距離大於A到C然後到B的距離,那麼,這個C點就可以放入最短路。與此相同,如果有的可以放入1號點,有的放入對最短路沒有改善,那麼d陣列就是儲存的任意兩個點之間的最短路。

然後迴圈繼續k = 2,那麼,又開始在任意兩個點之間嘗試吧2號點放入最短路,依然是,有的可以,有的不可以。但是,因為剛才d陣列已經不是開始的d陣列了,開始的d陣列是任意兩個點直接相連的最短路,現在的是不超過1個點的最短路。而嘗試把2號點放入則會讓d陣列變成任意兩個點的中間點不超過2的最短路。

以此類推,知道把所有點迴圈完,就認為d陣列是儲存的任意兩個點之間中間點不超過n個點的最短路,實際上,這時候的d已經是站在全域性上考慮的d了,而且對於一個n個點的圖來說,他的最短路的中間點一定小於n,那麼我們就認為,這時候的任意兩個點的最短路就是實際上要求的任意兩個點的最短路。

演算法的魅力不在於用這個演算法A掉了一個或更多的題目,而是這個演算法本身!


歡迎到微信裡去當吃瓜群眾