Floyd算法学习笔记

news/2024/11/20 1:21:42/

Floyd算法学习笔记

前言

同步于 c n b l o g s cnblogs cnblogs 发布

如有错误,欢迎各位 dalao 批评指出。

前置芝士:

1.邻接矩阵(Floyd要用邻接矩阵存图)

2.动态规划思想(最好学过,没学过也没有太大影响)

1. Floyd 所解决问题的类型

我们可以发现,如 Dijkstra,SPFA,Bellman Ford 一类的最短路算法都是解决单源点最短路问题,也就是确定了起点或者终点来求最短路的问题。但是,我们发现,这些算法解决多源点最短路问题,也就是有多个起点和终点的最短路问题 ,的效率太低。假设有 n n n 个点, m m m 条边。解决多源最短路时,如果用以上三种算法来解决,都需要分别做 n n n 次,来求解以每个点为起点的单源最短路,时间复杂度最慢分别是 O ( n m l o g n ) , O ( n 2 m ) , O ( n 2 m ) O(nmlogn),O(n^2m),O(n^2m) O(nmlogn),O(n2m),O(n2m),在稠密图 m = n ∗ ( n − 1 ) / 2 ≈ n 2 m=n*(n-1)/2≈n^2 m=n(n1)/2n2其中最快的都需要 O ( n 3 l o g m ) O(n^3logm) O(n3logm) ,最慢的甚至是 O ( n 4 ) O(n^4) O(n4) ,效率太低。因此,我们今天的主角 Floyd 就因解决多源最短路问题而诞生了!

2. Floyd 的思想和基本做法

Floyd算法的基本思想是动态规划。

d p i j dp_{ij} dpij 表示 i i i j j j 的最短距离。(有 n n n 个点)

首先对于这个 d p dp dp 数组的初始化就是将输入的边 x − y x-y xy 权值为 z z z (无权图就是 1 1 1),如果图是无向,则 d p x y = d p y x = z dp_{xy}=dp_{yx}=z dpxy=dpyx=z ,如果图是有向,则 d p x y = z dp_{xy}=z dpxy=z,最后将所有 d p i i = 0 ( 0 ≤ i ≤ n ) dp_{ii}=0 (0\le i\le n) dpii=0(0in),比较显然,这里不做解释。

接着我们进行状态转移。显然,我们要转移 d p i j dp_{ij} dpij,就需要找一个点 k k k,来进行转移,也就是 d p i j ← m i n ( d p i j , d p i k + d p k j ) dp_{ij}\gets min(dp_{ij},dp_{ik}+dp_{kj}) dpijmin(dpij,dpik+dpkj),其中 d p i k + d p k j dp_{ik}+dp_{kj} dpik+dpkj 就表示 i − k i-k ik 的最短路与 j − k j-k jk 的最短路之和,其实也就相当于一个松弛操作。

这里特别要注意的是:我们的 k k k 那一层循环一定要放在 i i i j j j 两层循环之外,因为如果放在 i , j i,j i,j 以内的话,你就会发现每两个点的最短路只会被算到一次,而当你在进行状态转移时,你只算到一次的话,你会发现有些点在转移时,还没有被更新,就会出现没有求出最短路的情况,所以, k k k 的循环要放到 i , j i,j i,j 的循环之外。

Floyd算法由于 i , j , k i,j,k i,j,k 都要枚举一层循环,所以时间复杂度为 O ( n 3 ) O(n^3) O(n3),比开头讲的三个算法要快。

3.Floyd 代码实现

//n表示有n个点 
memset(dp,0x3f,sizeof(dp));//赋一个极大值,并且防止在转移时不溢出。
//接下来进行边的初始化和dp[i][i]=0
............
//状态转移 
for(int k=1;k<=n;++k)//枚举k 
{for(int i=1;i<=n;++i)//枚举i {if(i==k)//如果i,k个点相等,则不需要更新 continue;for(int j=1;j<=n;++j){if(i==j||k==j)//同第八行 continue;dp[i][j]=min(dp[i][k]+dp[k][j],dp[i][j]);//状态转移。 }}
}

4.Floyd 算法的一些其他应用

(1).Floyd 输出路径

对于输出路径,我们可以定义一个 p a t h i j path_{ij} pathij 表示 d p i j dp_{ij} dpij 是有 d p i p a t h i j + d p p a t h i j j dp_{ipath_{ij}}+dp_{path_{ij}j} dpipathij+dppathijj 转移而来。并且在一开始,我们将所有 p a t h path path 数组的元素赋一个特殊值。(假定为 − 1 -1 1 )

显然,要输出 i − j i-j ij 的路径,我们可以通过递归来解决。

具体代码如下:

//赋特殊值并且做 floyd
-----------
//输出路径函数
void print(int i,int j)//print(a,b) 表示输出a,b的最短路径
{if(path[i][j]==-1)//因为开始初始化为-1,这里就可以避免相邻的再次输出 return;print(i,path[i][j]);//前半部分cout<<path[i][j]<<"-->";//输出该点 print(path[i][j],j);//后半部分
}

(2).Floyd 判断负环

在说这一个应用之前,我们先来讲一下负环的定义。

负环,就是指一个图中,存在一个环,使得这个环的权值之和为负数,这个环就是负环。

例如下图:

qwq

可以发现,由图中三个点组成的一个环的权值之和为负数,这就是一个简单的负环。

一个图中一旦存在负环,环里的两个点之间的最短路可以被无限更新,因为为了使得路径长度最短,它可以一直走这个负环,无限循环,这个最短路径就可以无限缩小。也就是说,一个图一旦存在负环,它的最短路就是 − ∞ -\infty ,也就相当于无解。

虽然说 SPFA Bellmanford 两个最短路算法可以判断负环,但是我们还是要讲一讲 Floyd 算法判断负环的方法。

因为负环可以使得两个点之间的最短路无限变小,所以我们可以发现,我们可以做两次 Floyd ,第一次来更新所谓的最短路,然后再做第二次的时候,如果两个点之间的最短路还可以被更新,就可以说明这个图中存在负环,比较显然。(自己想一想)。

第二次 Floyd 代码实现:

for(int k=1;k<=n;++k)
{for(int i=1;i<=n;++i){if(i==k)//同第一次continue;for(int j=1;j<=n;++j){if(i==j||j==k)//同上continue;if(dp[i][j]>dp[i][k]+dp[k][j])//如果可以被更新,则存在负环{puts("No solution!");}}}
}

(3).Floyd 判断有向图的连通性

我们知道,对于无向图的两个点是否联通,我们可以运用并查集来判断两个点是否联通。

但是对于一个有向图,并查集是不能够维护的,所以这个时候,我们就再一次请出今天的主角 Floyd 来判断两个点 i , j i,j i,j 是否连通。

我们定义 d p i j dp{ij} dpij 表示 i , j i,j i,j 是否连通,若联通则为 1 1 1 ,不连通则为 0 0 0

显然,我们可以根据 Floyd 一一样的方式进行初始化。一开始除了 d p i i dp_{ii} dpii 全部赋值为 0 0 0 ,输入边的时候直接初始化即可。

接下来就是状态转移。我们可以模仿普通的 Floyd ,再找一个点 k k k ,如果 i i i 可以到达 k k k , k k k 可以到达 j j j ,就可以说明 i i i 可以到达 j j j ,转化成转移方程就是 d p i j ∣ = d p i k & d p k j dp_{ij}|=dp_{ik}\&dp_{kj} dpij=dpik&dpkj

核心代码:

//n表示有n个点 
memset(dp,0,sizeof(dp));//赋值。
//接下来进行边的初始化和dp[i][i]=1
............
//状态转移 
for(int k=1;k<=n;++k)//枚举k 
{for(int i=1;i<=n;++i)//枚举i {if(i==k)//同上continue;for(int j=1;j<=n;++j){if(i==j||k==j)//同上continue;dp[i][j]|=dp[i][k]&dp[k][j];//状态转移。 }}
}

关于 Floyd 算法的讲解就到这里了, s e e y o u see~ you see you


http://www.ppmy.cn/news/761951.html

相关文章

scanpy sc.pp.normalize_per_cell bug

今天遇到一个很奇怪的bug, 当今天跑covid_atlas数据集的时候&#xff0c;在123服务器总是报错&#xff0c;但是我记得在122服务器上是跑过没问题的 最终的测试结果如下 import scanpy as sc import numpy as np from QUEST import QUEST from QUEST.utils import get_free_…

steam网站服务器无法使用,steam没法连接服务器,解决方法是什么?

你看视频上网也掉线吗&#xff1f;如果也掉线&#xff0c; 可能有以下原因&#xff1a;1.线路问题&#xff0c;确保线路连接正确&#xff0c;线路通讯质量良好没有被干扰&#xff0c;如用分线盒&#xff0c;则选用质量较好的。2.网卡问题&#xff1a;选择质量比较好的网卡3.系统…

【网络编程】多个服务器的情况:nginx实现反向代理、nginx基于反向代理实现负载均衡

如果我们有多个服务器&#xff0c;比如我们只有一个域名&#xff1b;我们可以利用其中一台服务器&#xff0c;通过nginx为这一个域名实现反向代理&#xff1b;进一步&#xff0c;我们可以利用这多台服务器&#xff0c;为这一个域名基于nginx的反向代理实现负载均衡。 文章目录 …

Linux:Active: active (exited) since Thu 2023-07-06 07:06:51 UTC; 1h 57min ago

上述信息表示&#xff0c;ufw服务在您的系统上处于活动状态&#xff0c;并且已经成功启动。状态中显示为"exited"表示该服务是一次性运行的&#xff0c;并且它没有正在运行的进程。 如果您想关闭 ufw 防火墙服务&#xff0c;可以执行以下命令&#xff1a; sudo sys…

Windows如何设置自动关闭未响应的程序?Windows设置自动关闭未响应的程序方法,带图详解

Windows系统程序经常出现程序未响应现象&#xff0c;如何通过注册表使其自动关闭呢 1、首先快捷键winR唤出【运行】 输入regedit 2、确定后就打开了注册表编辑器&#xff0c;定位到【HKEY_CURREnT_UsER\Control panel\desktop】项下 3、在右侧找【AutoEndTasks】数值数据&#…

latex和word对齐 1.5倍行距

latex和word对其 1.5倍行距 {\heiti \fontsize{12pt}{1.5}\textbf{}} \vspace{1.5em}\ {\heiti \fontsize{16pt}{1.5}\textbf{}} \vspace{1.5em}\ {\heiti \fontsize{16pt}{1.5}\textbf{}} \vspace{1.5em}\

为什么在wps中调整了0.5倍行距,某一页的行数不会发生变化?

本人写软著的时候&#xff0c;要求每行不少于50行代码&#xff0c;但是我在文档上怎么调整也不到50行&#xff0c;即使调整到0.5到行距也不会发生变化&#xff0c;最多40多行的样子&#xff0c;最后查了查是因为定义网格的原因。 只需要在上面那个“如果定义了文档网格&#xf…

解决在word中插入Mathtype公式后行距变大的问题(简单有效)

问题描述 又到了写论文的季节&#xff0c;最近遇到这样的问题&#xff0c;在word中插入MathType公式时&#xff0c;行距变大&#xff0c;原来的单倍行距已经不像只有文字时的单倍行距了&#xff0c;如下图&#xff1a; 解决方法 选中该段落打开段落取消 如果定义了文档网络…