Floyed(floyd)算法詳解


是真懂還是假懂?

Floyed算法:是最短路徑算法可以說是最慢的一個。

原理:O(n^3)的for循環,對每一個中間節點k做松弛(尋找更短路徑);
但它適合算多源最短路徑,即任意兩點間的距離。
但spfa,迪傑斯特拉就只能算一個點到其他任一點的最短路徑。
 
關鍵在於,我們真的真正理解floyed嗎?
 
就是因為它太短了,以至於我們有些人(神仙除外)看代碼后看到這樣一個語句:
 
d[i][j]=min(d[i][j],d[i][k]+d[k][j])
 
也就是說,對於每一個中轉點k來說,進行O(n^2)的松弛,一定能找到最短路徑。
 
雖不是最優,但是松弛次數來湊!
 
這大概就是我們(之前的我)的無須證明的,想當然的理解,並背下來了它,以便以后想TLE時用。(???O(n^3));
so?
Floyed本質是dp;
 
遞推公式:(圖片源自網絡大佬)

眾所周知,dp(動態規划)要滿足無后效性。也就是說。。。。。。

還是先舉個例子:

我們設k取某一個k1時滿足k1為最終點i到j最短路經過的點,但是在外層循環到k1時d[i][k1]和d[k1][j]並沒有取到最小值,因為k1只能取一次,那么往后再循環是不是就取不到k1了呢??

答案當然不是的(不然這個算法為什么正確?)

還是那句話,dp無后效性,

也就是說,k不單單是枚舉,還是一個狀態變量,找i和j之間通過編號不超過k(k從1到n)的節點的最短路徑(一定要注意,這里是當前最短路徑,

k之前的已經變成最短路了,對於每一個k,我們都進行了n^2的充分枚舉(ij),已保證當前已經滿足對從1到k的節點最優,

那么當k枚舉完所有點,那么一定是最優的了

換句話說,在d[i][j]=min(d[i][j],d[i][k]+d[k][j])

公式中,因為k之前已經作為i或者j被枚舉過了;,d[i][k]和d[k][j] 已經被1到k枚舉過了

那么他們一定是1到k節點中最優的路徑,等枚舉到n時,所有的都枚舉完了,那么它們就是
 
基本代碼:
 
for(k=1;k<=n;k++) //中轉節點 
     for(i=1;i<=n;i++) 第二層循環
         for(j=1;j<=n;j++)  第三層循環
                if(e[i][j]>e[i][k]+e[k][j] )如果直接到達比通過k這個中轉接點到達的距離短   
                     e[i][j]=e[i][k]+e[k][j];那么就更新松弛

 算法復雜度O(n^3),這也是為什么平常很少使用的原因。

 

例題校內題目:

 

城市交通費
【問題描述】
有 n 個城市,編號 1~n。其中 i 號城市的繁華度為 pi。省內有 m 條可以雙向同行的高速
公路,編號 1~m。編號為 j 的高速公路連接編號為 aj 和 bj 兩個城市,經過高速公路的費用
是 wj。若從城市 x 出發到某城市 y,除了需要繳納高速公路費用,還要繳納“城市建設費”
(為從 x 城市到 y 城市所經過的所有城市中繁華度的最大值,包括 x 和 y 在內)。
現提出 q 個詢問,每個詢問給出一組 x 和 y,你需要回答從 x 出發到 y 城市,所需要的
最低交通費(高速公路費+城市建設費)是多少。
【輸入】
第一行三個整數 n,m,q。
第二行 n 個整數,表示 p1~pn。
接下來 m 行中,每行 3 個正整數,第 j 行包含 Aj,Bj,Wj。
隨后 Q 行每組兩個正整數 x,y 表示一組詢問。
【輸出】
共 Q 行,為對 Q 個問題的回答:x 城市到 y 城市的最小交通費用。
【樣例輸入】
5 7 2
2 5 3 3 4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 4
2 3
【樣例輸出】
8
9
【數據范圍及約定】
n≤250,m≤20000,Q≤10000,Pi≤10000,Wj≤2000,保證任意兩個城市可以互相到達。
【樣例說明】
圖中,代表城市的格子中第一個數字是城市編號,第二個紅色數字是該城市的繁華度。
(1)從城市 1 到城市 4 的最小交通費用路線是:1 3 5 4;公路費是 2+1+1=4;城市建設費是
max{2,3,4,3}=4;總交通費用 4+4=8。
(2)從城市 2 到城市 3 的最小交通費用路線是:2 5 3;公路費是 3+1=4;城市建設費是
max{5,4,3}=5;總交通費用 4+5=9。

這個題看起來就是一個最短路問題。
 
問題是Q(詢問次數)的范圍到了一萬,一萬次詢問,不管是spfa還是堆優化迪傑斯特拉,都得爆炸。
 
那么,我們平時鳥都不鳥的Floyed就排上了用場;
 

但題目中給的點數為250,三次方為15625000,不會爆TLE,

可以使用,對於一萬次詢問,O(1)詢問就可以過了。

但是,這個題目有一個附加條件:繁華度。

怎樣在floyed算法中加入繁華度來考慮呢?

 代碼:(注意floyed部分)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,q,p[300],aj,bj,wj,x,y,f[300][300],a[300][300],top,t[300];
int cmp(int x,int y)
{
    return p[x]<p[y];
}
int main()
{
    memset(a,63,sizeof(a));
    top=0;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)//正常輸入
        scanf("%d",&p[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&aj,&bj,&wj);
        a[aj][bj]=min(a[aj][bj],wj);//初始化
        a[bj][aj]=min(a[bj][aj],wj);//這是鄰接矩陣類型的,沒用鏈式前向星
    }
    for(int i=1;i<=n;i++)
    {
        a[i][i]=0;//對角線置為0
        t[i]=i;//編號
    }
    sort(t+1,t+1+n,cmp);//t數組開始時是編號,但經過sort排序后就變成了城市繁華度從小到大的順序
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            f[i][j]=a[i][j]+max(p[i],p[j]);//f數組即為答案數組,這里初始化
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                a[i][j]=min(a[i][j],a[i][t[k]]+a[t[k]][j]);//a數組就是最短路數組
                f[i][j]=min(f[i][j],a[i][j]+max(p[i],max(p[j],p[t[k]])));
f數組就是答案數組,a數組不受f數組影響,有可能a更新了,但是f將最大繁華值考慮進去后並沒有更新,那么a數組保留最短路為以后的更新做鋪墊
}
for(int i=1;i<=q;i++) { scanf("%d%d",&x,&y); printf("%d\n",f[x][y]); } return 0; }

 

對Floyd的dp性質更進一步理解

完結撒花。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM