首页 > 编程语言 >Floyd算法学习笔记

Floyd算法学习笔记

时间:2024-03-19 21:57:02浏览次数:15  
标签:短路 ij 笔记 负环 算法 Floyd dp

Floyd算法学习笔记

前言

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

前置芝士:

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

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

1. Floyd 所解决问题的类型

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

2. Floyd 的思想和基本做法

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

设 \(dp_{ij}\) 表示 \(i\) 到 \(j\) 的最短距离。(有 \(n\) 个点)

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

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

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

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

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 输出路径

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

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

具体代码如下:

//赋特殊值并且做 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\) 是否连通。

我们定义 \(dp{ij}\) 表示 \(i,j\) 是否连通,若联通则为 \(1\) ,不连通则为 \(0\) 。

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

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

核心代码:

//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 算法的讲解就到这里了,\(see~ you\) 。

标签:短路,ij,笔记,负环,算法,Floyd,dp
From: https://www.cnblogs.com/SFsaltyfish/p/18084054

相关文章

  • dijkstra算法详解
    今天给大家讲解\(dijkstra\)图论最短路算法在讲解\(dijkstra\)算法之前,先来给大家讲解一下图论中的松弛操作。松弛,即\(relaxtion\),是一种编程学术语。举例说明,例如我们可以从某个机场坐飞机达到若干个机场,然后从这些机场出发,我们又需做火车前往若干个城镇。现在假设我们手里......
  • 代码随想录算法训练营day28 | leetcode 93. 复原 IP 地址、78. 子集、90. 子集 II
    目录题目链接:93.复原IP地址-中等题目链接:78.子集-中等题目链接:90.子集II-中等题目链接:93.复原IP地址-中等题目描述:有效IP地址正好由四个整数(每个整数位于0到255之间组成,且不能含有前导0),整数之间用'.'分隔。例如:"0.1.2.201"和"192.168.1.1"是有效IP......
  • 【蓝桥杯选拔赛真题70】python最短路径和 第十五届青少年组蓝桥杯python选拔赛真题 算
    目录python最短路径和一、题目要求1、编程实现2、输入输出二、算法分析三、程序编写四、程序说明五、运行结果六、考点分析七、 推荐资料1、蓝桥杯比赛2、考级资料3、其它资料python最短路径和第十五届蓝桥杯青少年组python比赛选拔赛真题一、题目要求(注:i......
  • 监督学习算法——决策树
    本篇承接上篇文章监督学习算法——线性模型决策树importsyssys.pathmglearn.plots.plot_animal_tree()1.构建决策树我们在下图所示的二位分类数据集上构造决策树。这个数据集由2个半月形组成,每个类别都包含50个数据点。我们将这个数据集称为two_moons。学习决策......
  • raft算法和etcd代码解析-1.raft基本概念
    笔记导言该系列笔记用于GO语言和RAFT算法学习前部分介绍raft算法后部分介绍etcd代码etcd源码来自github,版本主要为ectd-3.1.5本文主要根据视频:<<raft算法工程案例之etcd源码导读>><<解析分布式共识算法之Raft算法>>以上视频作者主页:https://space.bilibili.com/317473362......
  • 【深度学习】深度学习md笔记总结第1篇:深度学习课程,要求【附代码文档】
    深度学习笔记完整教程(附代码资料)主要内容讲述:深度学习课程,深度学习介绍要求,目标,学习目标,1.1.1区别。TensorFlow介绍,2.2图与TensorBoard学习目标,2.2.1什么是图结构,2.2.2图相关操作,2.2.3TensorBoard:可视化学习。TensorFlow介绍,2.4张量学习目标,2.4.1张量(Tensor),2.4......
  • 【云开发笔记No.4】DevOps的起源,定义和基本原则
    DevOps,作为一组过程、方法与系统的统称,它的出现并不是偶然的,而是源于软件开发与运维领域长期以来所面临的挑战和痛点。其诞生背景可以追溯到敏捷开发模式的兴起以及持续开发所带来的运维问题。随着软件行业的飞速发展,传统的软件开发与运维模式逐渐暴露出沟通不畅、效率低下等......
  • 高精度算法(大数的加、减、乘、除)
    在C/C++中,int占一个机器字长,32位机中则占4个字节,即[-2^31,2^31-1](10的9次方数量级)。不管是32位还是64位机,longlong占8个字节,即[-2^63,2^63-1](10的18次方数量级)。如果超过该数量级,应该使用高精度算法。1加1、将两个加数逆序存储在两个int数组中。(逆序的原因是方便操作和数......
  • 实现数据结构与算法学习笔记(java)——顺序表顺序栈代码实现
    顺序表实现顺序栈实现......
  • 蓝桥杯单片机快速开发笔记——超声波测距
    一、原理分析        超声波测距是一种常见的测距方法,其原理是利用超声波在空气中传播的速度恒定且较快的特性,通过发送超声波信号并接收回波,计算出物体与传感器之间的距离。以下是超声波测距的原理和应用:原理:发送超声波信号:超声波传感器发送一个短脉冲的超声波信......