Part I-Introduction

Floyd算法是一种求图上多源最短路径的算法,适用于中小规模的图,思维简单易懂。

Floyd算法的实质是(区间)动态规划,在这里做一个简单的概述。

对于一个有\(n\)个结点的图,

令\(dis[i][j]\)为结点\(i\)到结点\(j\)的最短路径长度。

首先,将所有现成的边都存入\(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\}\]

代码实现:

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为现存的边。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:循环时\(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])\),降维得到现在的方程。

时间复杂度:\(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\)之间的最短路的中转点就要发生改变,即\(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]\):此时的中转点无法得到更优的解,忽略。

这样我们就处理好了\(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

题面:

这题貌似比上一题更复杂——要进行路径计数。

令\(cnt[i][j]\)为结点\(i,j\)之间的最短路径条数

还是在处理\(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]\):此时的中转点无法得到更优的解,忽略。

处理完\(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:最小环问题

来看这么一个问题:http://acm.hdu.edu.cn/showproblem.php?pid=1599

题面:

看似无从下手,但仍然是Floyd

在更新\(dis\)前,我们已经把\(1-(k-1)\)的情况处理好了。那么当前的最小环就是:

\[\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\)的循环范围的控制,因为\(i,j,k\)不能相同。

至于为什么先处理最小环在更新路径,当然是为了使\(dis[i][j]\)不经过\(k\)啊。

转载于:https://www.cnblogs.com/-Wallace-/p/11165803.html

Floyd算法及其应用相关推荐

  1. 数据结构与算法(7-4)最短路径(迪杰斯特拉(Dijkstra)算法、弗洛伊德(Floyd)算法)

    目录 一.最短路径概念 二.迪杰斯特拉(Dijkstra)算法(单源最短路径) 1.原理 2.过程 3.代码 三.弗洛伊德(Floyd)算法(多源最短路径) 1.原理 2.存储 3.遍历 4.代码 参 ...

  2. 【POJ/算法】 3259 Wormholes(Bellman-Ford算法, SPFA ,FLoyd算法)

    Bellman-Ford算法 Bellman-Ford算法的优点是可以发现负圈,缺点是时间复杂度比Dijkstra算法高.而SPFA算法是使用队列优化的Bellman-Ford版本,其在时间复杂度和编 ...

  3. 最小环算法求解(Dijkstra算法+Floyd算法)

    方法一: #include<iostream> #include<algorithm> #include<cmath> #include<cstdio> ...

  4. HDU2544(Bellman-ford算法和Floyd算法)

    思路: 1.初始化时将起点 s 到各个顶点 v 的距离 dist(s->v) 赋值为 ∞,dist(s->s) 赋值为 0: 2.后续进⾏最多 n-1 次遍历操作 (n 为顶点个数), 对 ...

  5. 【图论专题】Floyd算法及其扩展应用

    Floyd的拓展应用: 任意两点最短路 传递闭包 找最小环 恰好经过k条边的最短路(倍增) 题目列表: 题目 算法 AcWing 1125. 牛的旅行 任意两点最短路Floyd AcWing 343. ...

  6. 【图论】用一道题从本质上讲清楚Floyd算法

    P1119 [灾后重建] 4 5 1 2 3 4 0 2 1 2 3 1 3 1 2 2 1 4 0 3 5 4 2 0 2 0 1 2 0 1 3 0 1 4 -1 -1 5 4 一道非常好的Flo ...

  7. 图的单源最短路径,Floyd算法(数据结构c++)

    这个算法结构很是简单,但是理解还是有一定的困难,一开始做的时候想不明白,跟着算法自己动手画画就知道这个算法具体是怎么回事了. 时间复杂度是O(N*3) 算法有点动态规划的意思,有两个数组,一个(dis ...

  8. floyd算法_最短路径的算法:Floyd算法

    点击箭头处"蓝色字",关注我们哦!! 算法 最短路径的算法-Floyd算法 ● ○ ● Shortest Path Algorithm - Floyd Algorithm ● ○ ...

  9. 最短路径—Dijkstra算法和Floyd算法

    Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Di ...

  10. 最短路径-Dijkstra算法与Floyd算法

    最短路径-Dijkstra算法与Floyd算法 原文:https://www.cnblogs.com/smile233/p/8303673.html 一.最短路径 ①在非网图中,最短路径是指两顶点之间 ...

最新文章

  1. c++引用的自我见解
  2. mysql emma 使用教材_emma的使用
  3. oracle数据库怎么导出dat文件_oracle导入导出dmp文件(详细步骤)
  4. DuerOS Java开发技能(二)第三方授权(OAUTH2.0)
  5. 平衡不完全区组设计 数据分析的SAS实践
  6. 我的2012年度总结
  7. BAT资深算法工程师《深度学习》读书分享:概率和信息论
  8. Codeforces Round #700 (Div. 1) C. Continuous City 构造 + 二进制
  9. PowerShell 笔记
  10. 使用windows自带的网络命令工具抓包
  11. 50: 加密与解密 、 AIDE入侵检测系统 、 扫描与抓包 、 总结和答疑
  12. EasyUI美化界面项目系统(整合EasyUI_Insdep皮肤)
  13. Ubuntu修改hosts文件
  14. Hit Refresh读书摘要
  15. 汇编实现吃豆子小程序
  16. identifier “ “ is undefined 错误
  17. Wampserver图标是橙色的【问题与解决方案】
  18. 基因家族分析⑤:进化树构建
  19. 有序的uuid(32位)
  20. c语言联机游戏,C/C++ 游戏 贪吃蛇双人对战版

热门文章

  1. 脚手架 - props
  2. matlab时域转复频域,信号与系统实验(MATLAB版)实验15连续系统的复频域分析.ppt...
  3. php cgi漏洞,Nginx + PHP CGI的一个可能的安全漏洞
  4. 排序算法 —— 堆排序
  5. Servlet入门总结
  6. 蓝桥杯大赛基础之--数列排序
  7. 小程序 const moment = require('moment')_开源小程序精选
  8. anaconda下安装python,Windows下Anaconda的安装和简单使用方法
  9. python子进程kerberos_Anaconda3的python找不到kerberos凭证缓存
  10. python argparse_Python 命令行之旅:argparse、docopt、click 和 fire 总结篇