日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

图论算法(二)最短路算法:Floyd算法!

發布時間:2024/8/26 综合教程 35 生活家
生活随笔 收集整理的這篇文章主要介紹了 图论算法(二)最短路算法:Floyd算法! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最短路算法(一)

最短路算法有三種形態:Floyd算法,Shortset Path Fast Algorithm(SPFA)算法,Dijkstra算法。

我個人打算分三次把這三個算法介紹完。

(畢竟寫太長了又沒有人看QAQ……)但是這篇博客好像又雙叒叕寫的有點長,真的請各位耐心看完QAQ

今天先來介紹最簡單的Floyd算法。

Part 1:最短路問題是什么?

我們用專業一點的術語表達,大概是這樣子的:

若網絡中的每條邊都有一個數值(長度、成本、時間等),則找出兩節點(通常是源節點和阱節點)之間總權和最小的路徑就是最短路問題。

——摘自百度百科

但是完全不用關那些個專業的東西,我們通過字面意思大概就能Get到——求出某個點走到某個點之間的權值之和最小。

比如我們有一張圖:

比如我們要求出從點1到點5的最短路就是這樣的

點1->點2(走了4),點2->點5(走了1),這樣點1到點5的最短距離就是5,(因為找不到比5更短的路可以從點1到點5)最短路就是1->4->5。

Part 2:Floyd算法思路

在介紹Floyd算法之前,我們先思考這樣一個問題:

有一張圖,我們要求出最短路。怎么做?

我們可以間接的把它看成一個DP來搞,那么狀態就是這樣的:

我們枚舉點k,i,j,并假設i是起始點。

如果i->j的當前最短路長度 > i->k的最短路長度+k->j的最短路長度,我們就更新i->j的最短路長度是i->k+k->j。

什么意思呢?仔細思考一下,得出如下結果:

我們如果從某點直接到x點比先到y點再到x點的路徑之和還要長的話,當然就要更新“某點”到x點的最短距離啦!

對于這個思想,科學家已經給出了證明,我這里不再贅述。

(以下是證明過程,反正我是看不懂,但是NOI又不考算法為什么對,其實我們只要知道算法的正確性和怎么應用就好了,至于證明,那是數學家的事情)

Part 3:Floyd算法的各項性能數據、適用范圍、初始化注意事項

我們知道了Floyd算法的大致框架了,在康代碼之前,還有一個不能忽略的問題:它的適用范圍是什么。

(畢竟最短路有三種算法,算法競賽的時候不一定考哪種,萬一用錯了算法……)

適用范圍:存在負權邊但是沒有負權回路的有向圖、無向圖。(所有算法都不能解決負權回路,因為那樣根本不存在最短路)

時間復雜度O(n^3)時間復雜度要特別注意,當有500個點的時候就已經很危險了。

空間復雜度O(n^2)鄰接矩陣限制了空間復雜度不能再優化了。

結果調用方法:存在鄰接矩陣里,其中f[i][j]表示i->j的最短路長度。

算法主體代碼長度(不包括初始化等等):150B。

整個求最短路代碼長度:約600B(已經是最短路算法中最短的了)。

初始化注意事項:先把鄰接矩陣初始化為0x3f,然后把所有f[i][i]初始化為0。

Part 4:Floyd算法結構框架

具體思路和注意事項我上面已經講得很清楚了,我這里直接上模板代碼。

#include<algorithm>
#include<cstring>
#include<cstdio>
#define N 1010
void floyd()
{
    for(int k=1;k<=n;k++)//枚舉中間點k(一定一定是在最外層循環!) 
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
                dist[i][j]=max(dist[i][j],dist[i][k]+dist[k][j]);
                //如果更優,更新最優解 
}
int main()
{
    memset(dist,0x3f,sizeof(dist));//memset初始化 
    scanf("%d%d",&n,&m);//n點m邊圖 
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        dist[x][y]=z;
        dist[y][x]=z;//初始化無向圖鄰接矩陣 
    }
    for(int i=1;i<=n;i++)
        dist[i][i]=0;//對角線重新賦值為0 
    floyd();//調用Floyd解決最短路問題 
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            printf("%d ",dist[i][j]);//輸出所有i->j的最短路徑 
        }
        printf("
");
    }
    return 0;
}

Part 5:Floyd算法在實際問題中的應用

我在某洛谷上找到了這樣兩個題,很適合初學最短路算法的選手:

https://www.luogu.com.cn/problem/P2910

https://www.luogu.com.cn/problem/P1828

值得注意的是:這兩個題目不一定要用Floyd算法來解決,但是我們今天拿這兩個題簡單說說Floyd的實現注意事項。

首先看第一個題:洛谷P2910

雖然有超鏈接,我還是把題目的圖貼上來吧。

這個題目沒有直白的說“求最短路”(可能是板子題最后的尊嚴)但是明眼人都看出來了,出題人把權值抽象成了“危險程度”,規定了幾個節點是必須訪問的,求從出發點經過這些必須訪問的節點的最短路徑長度。

讀懂了題意之后,思考這樣一個問題:我們要用什么算法做這個題(這很重要!再次重申,最短路算法有三個,我們要選出最能對付這個題,在AC的前提下找出寫起來最簡單的算法)

首先看空間復雜度:點的個數N<=100也就是說,滿足鄰接矩陣的空間復雜度和時間復雜度。

必須到達的點有M<=10000個,也就是說,我們要求10000次單源最短路,Floyd算法可以一次性求出一張圖內任意兩點間的最短路。

綜上所述,這個題滿足使用Floyd算法的要求,并且我們說過,Floyd算法是最好寫的單源最短路算法(沒有之一!!!)

具體思路簡單BB一下,我們求出最短路之后,用存下“必須經過的點”的數組,把相鄰元素的最短路一次次累加,最后就可以得到最終答案。

上AC代碼:

//#include<guxinlin&sutang>
//GXL AK IOI
#include<algorithm>
#include<cstring>
#include<cstdio>
#define N 110
#define M 10010
#define sutang 0
using namespace std;
int v[N][N],vis[M],dis[N][N],n,m,ans;
void floyd()
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
int main()
{    
    scanf("%d%d",&n,&m);
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=m;i++)
        scanf("%d",&vis[i]);
    for(int i=1;i<=n;i++)
    {
        dis[i][i]=0;
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&v[i][j]);
            dis[i][j]=v[i][j];
        }
    }
    floyd();
    for(int i=2;i<=m;i++)
        ans+=dis[vis[i-1]][vis[i]];
    printf("%d",ans);
    return sutang;
}

下一個題目,洛谷P1828

先上題目截圖:

一眼看過去,節點數P<=800,800^3=512000000(5億一千二百萬)這個復雜度已經很危險了,但是為什么我們用Floyd算法AC掉了這個題呢?

下面是重點部分了,另外,上面的題的正解是n次堆優化dijkstra算法或者n次SPFA算法。

Floyd靈魂剪枝算法

其實這個題我本來沒有用Floyd算法做,(我是用的SPFA通過的評測)但是我看到題解去的大神們寫了這樣一篇題解,提到了Floyd靈魂剪枝。

我覺得是很有用的一個小技巧,而且實現起來非常簡單。于是我就把這個小技巧分享給大家。

具體思路是:如果給出的圖是無向邊,我們用鄰接矩陣存圖的時候是把無向邊當成兩條相反、長度相等的有向邊來存的,這就導致我們循環枚舉的的時候,更新了兩次這個無向邊,浪費了寶貴的時間。所以當圖是無向圖的時候,我們只需要枚舉一半的邊,更新的時候一下子更新兩條邊的最短距離,就可以優化一半的復雜度。(但是這個復雜度還是很高,只是一個小技巧,不是大優化)

說完了剪枝策略,我們來說說這個題的具體思路:

首先,每個節點可能有多個牛,我們就需要把第i頭牛在第j個牧場記下來。

其次,我們需要找出一個點,使其到這些有奶牛的點距離之和最小,確認是最短路算法。我們不知道哪個點距離和最小,所以我們需要把所有點到所有點的單源最短路求出來,然后枚舉每一個點的距離之和,找出最小值。找出所有點的單源最短路,Floyd算法可以做到。加上我們的剪枝策略,再加上O2優化,Floyd算法可以在1000ms內給出結果。

#include<algorithm>
#include<cstring>
#include<cstdio>
#define IAKIOI 0
#define N 1010
using namespace std;
int cow[N],dist[N][N],n,m,q,ans=99999999;
void floyd()
{
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)//枚舉一半,剩下的手動更新 
            {
                if(dist[i][j]>dist[i][k]+dist[k][j])
                {
                    dist[i][j]=dist[i][k]+dist[k][j];
                    dist[j][i]=dist[i][j];//手動更新另一條邊 
                }
            }
        }
    }            
}
int main()
{
    memset(dist,0x3f,sizeof(dist));//初始化 
    scanf("%d%d%d",&q,&n,&m);
    for(int i=1;i<=q;i++)//記錄第i頭牛所在牧場是cow[i] 
        scanf("%d",&cow[i]);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        dist[x][y]=z;//數據里沒有重邊,所以不用特判(其實是我忘了 
        dist[y][x]=z;
    }
    for(int i=1;i<=n;i++)//初始化 
        dist[i][i]=0;
    floyd();//調用Floyd解決問題 
    for(int i=1;i<=n;i++)
    {
        int qaq=0;
        for(int j=1;j<=q;j++)//枚舉第i個點的 
        {
            qaq+=dist[i][cow[j]];//累加第i個點的總路程 
        }
        ans=min(ans,qaq);//如果比最優答案還優,更新他
    }
    printf("%d",ans);//輸出答案 
    return IAKIOI;
}

好了今天的最短路學習分享就到這里,喜歡的記得三連QAQ(卑微博主求關注)

總結

以上是生活随笔為你收集整理的图论算法(二)最短路算法:Floyd算法!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。