日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

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

發(fā)布時(shí)間:2024/8/26 40 生活家
生活随笔 收集整理的這篇文章主要介紹了 图论算法(二)最短路算法:Floyd算法! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最短路算法(一)

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

我個(gè)人打算分三次把這三個(gè)算法介紹完。

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

今天先來介紹最簡(jiǎn)單的Floyd算法。

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

我們用專業(yè)一點(diǎn)的術(shù)語表達(dá),大概是這樣子的:

若網(wǎng)絡(luò)中的每條邊都有一個(gè)數(shù)值(長(zhǎng)度、成本、時(shí)間等),則找出兩節(jié)點(diǎn)(通常是源節(jié)點(diǎn)和阱節(jié)點(diǎn))之間總權(quán)和最小的路徑就是最短路問題。

——摘自百度百科

但是完全不用關(guān)那些個(gè)專業(yè)的東西,我們通過字面意思大概就能Get到——求出某個(gè)點(diǎn)走到某個(gè)點(diǎn)之間的權(quán)值之和最小。

比如我們有一張圖:

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

點(diǎn)1->點(diǎn)2(走了4),點(diǎn)2->點(diǎn)5(走了1),這樣點(diǎn)1到點(diǎn)5的最短距離就是5,(因?yàn)檎也坏奖?更短的路可以從點(diǎn)1到點(diǎn)5)最短路就是1->4->5。

Part 2:Floyd算法思路

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

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

我們可以間接的把它看成一個(gè)DP來搞,那么狀態(tài)就是這樣的:

我們枚舉點(diǎn)k,i,j,并假設(shè)i是起始點(diǎn)。

如果i->j的當(dāng)前最短路長(zhǎng)度 > i->k的最短路長(zhǎng)度+k->j的最短路長(zhǎng)度,我們就更新i->j的最短路長(zhǎng)度是i->k+k->j。

什么意思呢?仔細(xì)思考一下,得出如下結(jié)果:

我們?nèi)绻麖哪滁c(diǎn)直接到x點(diǎn)比先到y(tǒng)點(diǎn)再到x點(diǎn)的路徑之和還要長(zhǎng)的話,當(dāng)然就要更新“某點(diǎn)”到x點(diǎn)的最短距離啦!

對(duì)于這個(gè)思想,科學(xué)家已經(jīng)給出了證明,我這里不再贅述。

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

Part 3:Floyd算法的各項(xiàng)性能數(shù)據(jù)、適用范圍、初始化注意事項(xiàng)

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

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

適用范圍:存在負(fù)權(quán)邊但是沒有負(fù)權(quán)回路的有向圖、無向圖。(所有算法都不能解決負(fù)權(quán)回路,因?yàn)槟菢痈静淮嬖谧疃搪罚?/p>

時(shí)間復(fù)雜度O(n^3)時(shí)間復(fù)雜度要特別注意,當(dāng)有500個(gè)點(diǎn)的時(shí)候就已經(jīng)很危險(xiǎn)了。

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

結(jié)果調(diào)用方法:存在鄰接矩陣?yán)铮渲衒[i][j]表示i->j的最短路長(zhǎng)度。

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

整個(gè)求最短路代碼長(zhǎng)度:約600B(已經(jīng)是最短路算法中最短的了)。

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

Part 4:Floyd算法結(jié)構(gòu)框架

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

#include<algorithm>
#include<cstring>
#include<cstdio>
#define N 1010
void floyd()
{
    for(int k=1;k<=n;k++)//枚舉中間點(diǎn)k(一定一定是在最外層循環(huán)!) 
        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]);
                //如果更優(yōu),更新最優(yōu)解 
}
int main()
{
    memset(dist,0x3f,sizeof(dist));//memset初始化 
    scanf("%d%d",&n,&m);//n點(diǎ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;//對(duì)角線重新賦值為0 
    floyd();//調(diào)用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算法在實(shí)際問題中的應(yīng)用

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

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

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

值得注意的是:這兩個(gè)題目不一定要用Floyd算法來解決,但是我們今天拿這兩個(gè)題簡(jiǎn)單說說Floyd的實(shí)現(xiàn)注意事項(xiàng)。

首先看第一個(gè)題:洛谷P2910

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

這個(gè)題目沒有直白的說“求最短路”(可能是板子題最后的尊嚴(yán))但是明眼人都看出來了,出題人把權(quán)值抽象成了“危險(xiǎn)程度”,規(guī)定了幾個(gè)節(jié)點(diǎn)是必須訪問的,求從出發(fā)點(diǎn)經(jīng)過這些必須訪問的節(jié)點(diǎn)的最短路徑長(zhǎng)度。

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

首先看空間復(fù)雜度:點(diǎn)的個(gè)數(shù)N<=100也就是說,滿足鄰接矩陣的空間復(fù)雜度和時(shí)間復(fù)雜度。

必須到達(dá)的點(diǎn)有M<=10000個(gè),也就是說,我們要求10000次單源最短路,F(xiàn)loyd算法可以一次性求出一張圖內(nèi)任意兩點(diǎn)間的最短路。

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

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

上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;
}

下一個(gè)題目,洛谷P1828

先上題目截圖:

一眼看過去,節(jié)點(diǎn)數(shù)P<=800,800^3=512000000(5億一千二百萬)這個(gè)復(fù)雜度已經(jīng)很危險(xiǎn)了,但是為什么我們用Floyd算法AC掉了這個(gè)題呢?

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

Floyd靈魂剪枝算法

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

我覺得是很有用的一個(gè)小技巧,而且實(shí)現(xiàn)起來非常簡(jiǎn)單。于是我就把這個(gè)小技巧分享給大家。

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

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

首先,每個(gè)節(jié)點(diǎn)可能有多個(gè)牛,我們就需要把第i頭牛在第j個(gè)牧場(chǎng)記下來。

其次,我們需要找出一個(gè)點(diǎn),使其到這些有奶牛的點(diǎn)距離之和最小,確認(rèn)是最短路算法。我們不知道哪個(gè)點(diǎn)距離和最小,所以我們需要把所有點(diǎn)到所有點(diǎn)的單源最短路求出來,然后枚舉每一個(gè)點(diǎn)的距離之和,找出最小值。找出所有點(diǎn)的單源最短路,F(xiàn)loyd算法可以做到。加上我們的剪枝策略,再加上O2優(yōu)化,F(xiàn)loyd算法可以在1000ms內(nèi)給出結(jié)果。

#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++)//枚舉一半,剩下的手動(dòng)更新 
            {
                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];//手動(dòng)更新另一條邊 
                }
            }
        }
    }            
}
int main()
{
    memset(dist,0x3f,sizeof(dist));//初始化 
    scanf("%d%d%d",&q,&n,&m);
    for(int i=1;i<=q;i++)//記錄第i頭牛所在牧場(chǎng)是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;//數(shù)據(jù)里沒有重邊,所以不用特判(其實(shí)是我忘了 
        dist[y][x]=z;
    }
    for(int i=1;i<=n;i++)//初始化 
        dist[i][i]=0;
    floyd();//調(diào)用Floyd解決問題 
    for(int i=1;i<=n;i++)
    {
        int qaq=0;
        for(int j=1;j<=q;j++)//枚舉第i個(gè)點(diǎn)的 
        {
            qaq+=dist[i][cow[j]];//累加第i個(gè)點(diǎn)的總路程 
        }
        ans=min(ans,qaq);//如果比最優(yōu)答案還優(yōu),更新他
    }
    printf("%d",ans);//輸出答案 
    return IAKIOI;
}

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。