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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Floyd算法及其应用

發(fā)布時間:2025/3/15 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Floyd算法及其应用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Part I-Introduction

Floyd算法是一種求圖上多源最短路徑的算法,適用于中小規(guī)模的圖,思維簡單易懂。

Floyd算法的實質是(區(qū)間)動態(tài)規(guī)劃,在這里做一個簡單的概述。

對于一個有\(n\)個結點的圖,

\(dis[i][j]\)為結點\(i\)到結點\(j\)的最短路徑長度。

首先,將所有現(xiàn)成的邊都存入\(dis\),其余的令其值\(=\infty\),并使\(dis[i][i]=0\)

接著,枚舉中轉點(\(k\)),那么:

\[dis[i][j]=\min\{dis[i][k]+dis[k][j]\text{ | }k\in[1,n],k\ne i,k\ne j\}\]

代碼實現(xiàn):

void Floyd() {memset(dis,0x3f3f3f3f,sizeof(dis));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(a[i][j]!=0x3f3f3f3f)邊(i,j)存在。dis[i][j]=a[i][j];//a為現(xiàn)存的邊。for(int i=1;i<=n;i++) dis[i][i]=0;for(int k=1;k<=n;k++)//枚舉中轉點。for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||j==k||k==i) continue;dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);} }

Attention:循環(huán)時\(k\)必須放在第一層。若將\(i\)置于第一層,就會導致i->k->j的距離過早的確定下來,可能會導致答案錯誤。

注:其實最原始的Floyd中\(dis\)的定義是:\(dis[i][j][k]\)表示結點\(i\)到結點\(j\)只經過結點\(i-k\)的最短路徑。 則有:\(dis[i][j][k]=min(dis[i][j][k-1],dis[i][k][k-1]+dis[k][j][k-1])\),降維得到現(xiàn)在的方程。

時間復雜度:\(O(n^3)\)

Part II-Sevral Simple Problems

Floyd算法可以解決許多看似無法處理的問題。

Problem[1]:[USACO09JAN] Best Spot

鏈接:https://www.luogu.org/problemnew/show/P2935

題面:

此題較為簡單,算法流程:

  • 用Floyd處理出各個點間的最短路徑。

  • 計算出每個點到各個Favorites的總距離。

  • 選出總距離最小的點輸出即可。

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define R registerint a[501][501]; int dis[501][501]; int fav[501]; int P,F,C;void Floyd() {memset(dis,0x3f3f3f3f,sizeof(dis));for(R int i=1;i<=P;i++)for(R int j=1;j<=P;j++)if(a[i][j]!=0x3f3f3f3f)dis[i][j]=a[i][j];for(R int i=1;i<=P;i++) dis[i][i]=0;for(R int k=1;k<=P;k++)for(R int i=1;i<=P;i++)for(R int j=1;j<=P;j++){if(i==j||j==k||k==i) continue;dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);} }signed main() {scanf("%d%d%d",&P,&F,&C);memset(a,0x3f3f3f3f,sizeof(a));for(R int i=1;i<=F;i++) scanf("%d",fav+i);for(R int u,v,w,i=1;i<=C;i++){scanf("%d%d%d",&u,&v,&w);a[u][v]=a[v][u]=min(a[u][v],w);}Floyd();int minnum=0x3f3f3f3f,minid;for(R int i=1,sum=0;i<=P;i++,sum=0){for(R int j=1;j<=F;j++)sum+=dis[i][fav[j]];if(sum<minnum) minnum=sum,minid=i;}printf("%d\n",minid);return 0;}

Problem[2]:[JSOI2007]重要的城市

鏈接:https://www.luogu.org/problemnew/show/P1841

題面:

這一題比上一題略難,如何記錄中轉點?

Of course,在Floyd的時候。

\(c[i][j]\)為結點\(i,j\)之間的最短路的中轉點,若無則為0;

在進行對\(dis[i][j]\)的更新之時,我們不直接取min,而是先判斷以避免覆蓋。

因為我們還要進行一個更重要的操作,that is,更新\(c[i][j]\)

分情況討論:

  • 1.\(dis[i][j]>dis[i][k]+dis[k][j]\)(需要更新):此時結點\(i,j\)之間的最短路的中轉點就要發(fā)生改變,即\(c[i][j]=k\),并更新\(dis[i][j]\)的值。

  • 2.\(dis[i][j]=dis[i][k]+dis[k][j]\) :這不僅說明原先的\(c[i][j]\)已經失效,而且意味著此時已經不存在\(c[i][j]\)了(并不需要中轉點就有最短路了)。因此令\(c[i][j]=0\)

  • 3.\(dis[i][j]<dis[i][k]+dis[k][j]\):此時的中轉點無法得到更優(yōu)的解,忽略。

這樣我們就處理好了\(c\)。對于結果的處理,可以利用桶排序的思想,令\(res[c[i][j]]=1\)。(res[N]:bool)

最后遍歷\(res\),輸出答案(別忘了無解的處理!!!)。

#include<cstdio> #include<bitset> #include<cstring> using namespace std;const int N=205; int c[N][N]; int f[N][N]; int n,m;void Solve() {for(int i=1;i<=n;i++) f[i][i]=0;for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||j==k||i==k) continue;if(f[i][j]>f[i][k]+f[k][j])f[i][j]=f[i][k]+f[k][j],c[i][j]=k;else if(f[i][j]==f[i][k]+f[k][j])c[i][j]=0;} }bool res[N],flag=0; signed main() {memset(f,0x3f3f3f3f,sizeof(f));memset(c,0,sizeof(c));scanf("%d%d",&n,&m);for(int u,v,w,i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);f[u][v]=f[v][u]=w;//f:=dis}Solve();for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)res[c[i][j]]=1;for(int i=1;i<=n;i++)if(res[i]) printf("%d ",i),flag=1;if(!flag) puts("No important cities.");return 0; }

Problem[3]:[NOI2007]社交網絡

鏈接:https://www.luogu.org/problemnew/show/P2047

題面:

這題貌似比上一題更復雜——要進行路徑計數(shù)。

\(cnt[i][j]\)為結點\(i,j\)之間的最短路徑條數(shù)。

還是在處理\(dis\)的時候處理\(cnt\)

在分類討論之前,你需要知道一件事情:

假設我們已經處理完了\(cnt[i][k]\)\(cnt[k][j]\),那么怎么知道\(cnt[i][j]\)的值? 對于\(cnt[i][k]\)中的每條路徑,與\(cnt[k][j]\)配對,有\(cnt[k][j]\)條路徑。 那么\(cnt[i][k]\)條一起配對就是\(cnt[i][k]\times cnt[k][j]\)條,這就是\(cnt[i][j]\)的值。(說白了就是乘法原理)

那么,再開始分類討論:

  • 1.\(dis[i][j]=dis[i][k]+dis[k][j]\):又找到了一坨解,\(cnt[i][j]+=cnt[i][k]*cnt[k][j]\)(注意是+=!)。

  • 2.\(dis[i][j]>dis[i][k]+dis[k][j]\) :這代表有了新的解,原先的答案不能再用了,\(cnt[i][j]=cnt[i][k]*cnt[k][j]\)(注意是=!)。

  • 3.\(dis[i][j]<dis[i][k]+dis[k][j]\):此時的中轉點無法得到更優(yōu)的解,忽略。

處理完\(cnt\)后,那就意味著\(C_{s,t},C_{s,t}(v)\)什么的都出來了。

P.S.:建議cnt用double。

#include<cstdio> #include<algorithm> #include<cstring> using namespace std;const int N=105; int dis[N][N]; double cnt[N][N]; int n,m;signed main() {memset(dis,0x3f3f3f3f,sizeof(dis));memset(cnt,0,sizeof(cnt));scanf("%d%d",&n,&m);for(int u,v,w,i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);dis[u][v]=dis[v][u]=min(w,dis[u][v]);cnt[u][v]=cnt[v][u]=1;}for(int i=1;i<=n;i++)dis[i][i]=0;for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||i==k||j==k) continue;if(dis[i][j]==dis[i][k]+dis[k][j])cnt[i][j]+=cnt[i][k]*cnt[k][j];else if(dis[i][j]>dis[i][k]+dis[k][j]){dis[i][j]=dis[i][k]+dis[k][j];cnt[i][j]=cnt[i][k]*cnt[k][j];}}for(int k=1;k<=n;k++){double ans=0;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||i==k||j==k) continue;if(dis[i][j]==dis[i][k]+dis[k][j])ans+=cnt[i][k]*cnt[k][j]*1.0/cnt[i][j];}printf("%.3lf\n",ans);} }

Part III-EXT:最小環(huán)問題

來看這么一個問題:http://acm.hdu.edu.cn/showproblem.php?pid=1599

題面:

看似無從下手,但仍然是Floyd!

在更新\(dis\)前,我們已經把\(1-(k-1)\)的情況處理好了。那么當前的最小環(huán)就是:

\[\min\{a[i][k]+a[k][j]+dis[i][j]\}\]

先貼代碼:

#include<cstdio> #include<algorithm> #include<cstring> using namespace std;const int N=105; int a[N][N]; int dis[N][N]; int n,m;long long solve() {long long min_circle=0x3f3f3f3f;for(int k=1;k<=n;k++){for(int i=1;i<k;i++)for(int j=i+1;j<k;j++)min_circle=min(min_circle,1ll*a[i][k]+a[k][j]+dis[i][j]);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]);}return min_circle; }signed main() {while(scanf("%d%d",&n,&m)!=EOF){memset(a,0x3f3f3f3f,sizeof(a));for(int u,v,w,i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);a[u][v]=a[v][u]=min(a[u][v],w);}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=a[i][j];long long res=solve();if(res!=0x3f3f3f3f) printf("%lld\n",res);else puts("It's impossible.");}return 0; }

值得注意的是,\(i,j\)的循環(huán)范圍的控制,因為\(i,j,k\)不能相同。

至于為什么先處理最小環(huán)在更新路徑,當然是為了使\(dis[i][j]\)不經過\(k\)啊。

轉載于:https://www.cnblogs.com/-Wallace-/p/11165803.html

總結

以上是生活随笔為你收集整理的Floyd算法及其应用的全部內容,希望文章能夠幫你解決所遇到的問題。

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