Floyd算法:浅显外表下的动态规划内核

很久没遇到Floyd算法的题目了,2642. 设计可以求最短路径的图类刚好是一个典型。在实现核心算法之余,顺便整理一下算法的内核。

Floyd-Warshall’s Algorithm

Floyd-Warshall算法,简称Floyd算法,是“有向图非负权图的多源最短路”的经典算法和通用解法,以极其精炼的代码著称:

let dist be a |V| × |V| array of minimum distances initialized to ∞ (infinity)
for each edge (u, v) do
    dist[u][v] ← w(u, v)  // The weight of the edge (u, v)
for each vertex v do
    dist[v][v]0
for k from 1 to |V|
    for i from 1 to |V|
        for j from 1 to |V|
            if dist[i][j] > dist[i][k] + dist[k][j] 
                dist[i][j] ← dist[i][k] + dist[k][j]
            end if

算法核心的三层循环,最外层的k,作为串联首位节点ij的中间节点,其必须位于最外层,否则算法的正确性就遭到了破坏。由于整个迭代的主次序是由k决定的,就好像将中间节点一个一个地“插入”进来,所以Floyd算法又被称为“插点法”。这个不能随意改变次序的三层循环,实际上正是“动态规划”所严格强调的“子状态”和“顺序”的核心体现。

插点法与动态规划

从伪码上看,我们的整个动态规划的状态转移似乎是:
d i s t [ i ] [ j ] = m i n ( d i s t [ i ] [ j ] , d i s t [ i ] [ k ] + d i s t [ k ] [ j ] ) \displaystyle \mathrm {dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])} dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j])
但如果任意翻一本教科书,会找到的其实是另一种形式的转移方程:
d i s t [ i ] [ j ] [ k ] = m i n ( d i s t [ i ] [ j ] [ k − 1 ] , d i s t [ i ] [ k ] [ k − 1 ] + d i s t [ k ] [ j ] [ k − 1 ] ) \displaystyle \mathrm { dist[i][j][k] = min(dist[i][j][k-1], dist[i][k][k-1] + dist[k][j][k-1])} dist[i][j][k]=min(dist[i][j][k1],dist[i][k][k1]+dist[k][j][k1])
我们发现第三个维度被大多数写法有意无意地隐藏掉了,这其实是很常见的优化手段。但如果没看过原始版本的转移方程,就很容易误认为只有两个维度,迭代顺序也可以调换。那怎么理解这个原始转移方程呢?dist[i][j][k]实际上代表的是,我们定义一个中间节点集合S = {0, 1, 2, ..., k-1},让这k个点以任意顺序组合插入到ij之间时,从ij的最短路径长度;不失一般性,k = 0表示中间节点为空集合,那么dist[i][j][0]就是直接从i出发到j的边的长度。所以迭代过程中,我们实际上一个一个地将节点加入到集合S中,所以这个顺序是不能调换的。注意,我们说到中间节点的任意组合,实际上意味着多少种组合呢?能否理解好这点,决定了我们能不能彻底看清Floyd算法的本质。

插点与最短路

为了理解插点法的魅力,我们先来思考一下我们在处理的问题是一个怎样规模的系统。首先,在有向图里,我们能有多少条不重复的边呢?如果节点数为n,我们从每个节点出发,都能到达另外的n-1个节点,所以边的数量最多为n(n-1)。那么,我们能构成多少条不同的路径呢?有一个直觉是,如果不对这个问题加一个限定,它将导向+∞

因为这样的“富边图”里一定有环,只要有环,路径数就是无穷多的。但是我们可以很简单地加一个限定,就是找一找不经过重复节点的路径数量。因此,从ij的不经过重复节点的路径数量是另外n-2个节点的全排列组合的总和:

C n − 2 n − 2 ( n − 2 ) ! + C n − 2 n − 3 ( n − 3 ) ! + . . . + C n − 2 0 = ∑ k = 0 n − 2 C n − 2 k k ! \displaystyle \mathrm{C_{n-2}^{n-2}(n-2)! + C_{n-2}^{n-3}(n-3)! + ... + C_{n-2}^{0} = \sum_{k=0}^{n-2} C_{n-2}^{k}k!} Cn2n2(n2)!+Cn2n3(n3)!+...+Cn20=k=0n2Cn2kk!

其中k同前文所述,代表我们引入了k个插点。我们简单放大一下,发现单源情况下路径数量应该是(n-1)!级别:

∑ k = 0 n − 2 C n − 2 k k ! ≤ ( n − 1 ) ( n − 2 ) ! = ( n − 1 ) ! \displaystyle \mathrm{ \sum_{k=0}^{n-2} C_{n-2}^{k}k! ≤ (n-1)(n-2)! = (n-1)!} k=0n2Cn2kk!(n1)(n2)!=(n1)!

如果再枚举一下起点和终点,整个“多源最短路问题”的复杂度是 O ( n ! ) \displaystyle \mathrm {O(n!)} O(n!)级别,甚至大于指数级。那么Floyd算法能在多项式时间 O ( n 3 ) \displaystyle \mathrm {O(n^3)} O(n3)内,完成对该问题的解答,并且还如此精炼,无疑是动态规划的强大魔力。此外,我们仔细检查上面这些路径也恰恰是“最短路”的备选路径,因为我们可以简单用反证法证明,在路径中引入任意一个重复节点,都必然存在比其更优的路径。
在这里插入图片描述
如上图所示,如果路径中存在重复的中间节点,因为图里没有负权边,所以上面三条路径E1E2E3一定都大于等于0,那么必然有:
E 1 + E 3 < = E 1 + E 2 + E 3 \displaystyle \mathrm { E1 + E3 <= E1 + E2 + E3} E1+E3<=E1+E2+E3
则我们必然可以通过精简掉E2这段路达到一个相对更短的路径,所以存在重复节点的路径必然不是最短路。

拆解插点法

现在我们回到插点法本身,继续讨论插点集合S和路径数量之间的关系。通过前面的分析,我们已经知道这个路径数量随着插点的增加是阶乘级别地上升,但刚开始还是相当温和的,比如在不插入点和只插入一个点时,总共的路径也就两条:
在这里插入图片描述

那么,当k=2时,又如何呢?我们发现路径开始快速膨胀。
在这里插入图片描述
这里面我们发现通过一次状态转移,我们同时继承了插入一个以下节点的所有结果——例如,蓝色的路径其实是1j目前(插一点)所有的备选路径、红色路径其实是i0目前所有的备选路径。这些备选路径中的最短值,其实已经计算过了并且存储在 d i s t [ i ] [ 1 ] \displaystyle \mathrm{dist[i][1]} dist[i][1] d i s t [ 1 ] [ j ] \displaystyle \mathrm{dist[1][j]} dist[1][j]之中了,上面的图就是已经计算过的“路径的任意组合”,而所有最优、最短路径的再组合,就是Floyd算法动态规划中状态转移的实质。因此,在没有计算完所有插k-1点的组合之前,我们是绝对不可能计算插k点的最短路的。

总结

读者可以根据上面论述继续扩展,细细品味出其中的动态规划内核之精妙,也可以帮助我们更好地理解Floyd算法,避免强行进行记忆。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/492628.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第十届统计建模大赛 ——大数据与人工智能时代的统计研究数据解析

统计建模一般做法&#xff1a;可以采用统计分析方法&#xff0c;先进性数据预处理&#xff0c;再利用深度学习、神经网路方法进行预测。 1.碳排放预测&#xff1a;数据加代码 2.新能源汽车数据 3.太阳辐射数据 4.参考文献 请联系 建模忠哥小师妹

JavaScript 打印教程(第二部分)设置编码

JavaScript 打印教程&#xff08;第二部分&#xff09;设置编码 在进行文本打印时&#xff0c;尤其是涉及到中文或其他特殊字符时&#xff0c;正确的编码设置是非常重要的。不同的打印机支持不同的指令集&#xff0c;因此了解并使用适合您打印机的指令集是关键。本篇教程继续使…

【爬虫基础】第5讲 AJAX动态页面的数据获取

静态&#xff1a;访问地址栏里的数据就可以获取到想要的数据 动态&#xff1a;访问地址栏里的数据获取不到想要的数据 解决方案&#xff1a;抓包 打开浏览器的开发者工具-network-xhr,找到可以获取到数据的URL访问即可 获取url地址 代码实现&#xff1a; from urllib.request…

flask各种版本的项目,终端命令运行方式的实现

目录 写在前面 一、Flask项目的基本结构 二、使用终端命令运行Flask项目 1. 安装Flask 2. 创建Flask应用 3. 配置FLASK_APP环境变量 4. 运行Flask应用 5. 访问Flask应用 三、Flask CLI的其他功能 1. 创建Flask应用 2. 运行开发服务器 3. 清理缓存文件 4. 运行单元…

Spring6 (1)

Spring 1、简介&#xff1a;2、第一个程序2、set注入2.1 简单数据类型2.2测试2.3 注入Properties2.4 p命名空间注入2.5 c命名空间注入2.6 util注入2.6 引入外部配置文件 1、简介&#xff1a; 自己的理解&#xff1a;spring其实就是一个容器&#xff0c;也可以说是一个框架&…

Codeforces Round 936 (Div. 2) ---- E. Girl Permutation ---- 题解 (数论)

E. Girl Permutation&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 先理解什么是前缀最大值&#xff0c;他应该满足什么条件&#xff0c;根据定义可知对于 i 如果满足 所以 j < i&#xff0c;并且有 ai > aj&#xff0c;那么ai就是前缀最大值&#xff0c; 换…

大数据技术之 Apache Doris(一)

第 1 章 Doris 简介 1.1 Doris 概述 Apache Doris 由百度大数据部研发&#xff08;之前叫百度 Palo&#xff0c;2018 年贡献到 Apache 社区后&#xff0c;更名为 Doris &#xff09;&#xff0c;在百度内部&#xff0c;有超过 200 个产品线在使用&#xff0c;部署机器超过 10…

MySQL使用教程:数据库、表操作

目录 1. 免密码登录MySQL1.1 免密码配置1.2 登录选项介绍 2. MySQL基础配置&#xff1a;my.cnf3. 开机自启动设置&#xff08;可选设置&#xff09;4. 查看存储引擎5. 查看系统的编码规则和校验规则6. 数据库的操作6.1 查看数据库6.2 创建数据库 create database6.3 删除数据库…

九州金榜|面对校园霸凌,家长应该如何教育?

近期关于校园霸凌事件接连发生&#xff0c;前有邯郸时间&#xff0c;后有福建晋江一中学生因不忍被霸凌&#xff0c;选择跳楼轻生&#xff0c;面对此类事件&#xff0c;接连发生&#xff0c;孩子为什么会成为被霸凌的对象&#xff1f;家长应该如何教育孩子敢于对霸凌时说不。下…

【Java程序设计】【C00374】基于(JavaWeb)Springboot的社区疫情管理系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

Java Web-Tomcat

Web服务器 Web服务器是一个软件程序,对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是“提供网上信息浏览服务”。 Tomcat&#xff0c;是一个 HTTP 服务器。我们只需要在服务器中安装一个Web服务器如Tomcat&#xff0c;然后就可以将…

js逆向之对称加密west交大登录密码

目录 js逆向之对称加密&west交大登录密码 什么是DES? 什么是AES? 实例演示--某大学官网 找加密? 关键字搜索 第一处: 找到其加密码代码 下断点 扣代码 这js代码怎么运行呢? 如何使用node运行js代码? 下载这个加密算法对象库 引用(对象) 传参 联动pyth…

Rancher介绍

1.什么是Rancher Rancher是一套容器管理平台&#xff0c;专门用于部署和管理容器化应用。以下是关于Rancher的详细介绍&#xff1a; 容器编排与管理&#xff1a;Rancher是一个开源的企业级容器管理平台&#xff0c;它支持Kubernetes作为其容器编排引擎。Rancher可以帮助用户在…

rust中常用cfg属性和cfg!宏的使用说明,实现不同系统的条件编译

cfg有两种使用方式&#xff0c;一种是属性&#xff1a; #[cfg()]&#xff0c;一种是宏&#xff1a;cfg! &#xff0c;这两个都是非常常用的功能。 #[cfg()]是 Rust 中的一个属性 用于根据配置条件来选择性地包含或排除代码。cfg 是 "configuration" 的缩写&#xf…

将markdown文档中的图床外链图片下载到本地文件夹

markdown图床外链图片下载到本地代码 前言 因为文章发到先知或者攻防社区需要本地图片&#xff0c;而我的图片从来都是上传到图床&#xff0c;所以编写了一个脚本实现了把markdown文章中所有含有外链图床的图片转储到本地的文件夹。 然后发布文章时再手动一个个上传图片。 如果…

Set和Map数据结构

Set和Map数据结构理解 Set&#xff1a; 1、es6新的数据结构&#xff0c;类似数组&#xff0c;但成员唯一 2、实例属性&#xff1a;Set.prototype.size返回Set实例的成员总数 3、操作方法&#xff1a;add、delete、has、clear 4、遍历操作&#xff1a;forEach、keys、values、en…

【研发日记】C/C++开发避坑秘籍(一)——CAN接收Buffer溢出Bug

文章目录 背景介绍 问题描述 分析排查 解决方案 总结归纳 背景介绍 在一个嵌入式软件项目中&#xff0c;有一段使用C语言写的嵌入式代码&#xff0c;功能是把CAN总线上的几帧报文接收进来&#xff0c;并解析出数据。示例如下&#xff1a; 乍一看感觉挺简单&#xff0c;想着…

全球前十大交易所KuCoin遭美司法部、CFTC起诉!违反银行保护法、反洗钱!交2200万“保护费”还不够?

昨&#xff08;26&#xff09;日晚间&#xff0c;美国司法部释出重磅消息&#xff0c;全球排名前十的中心化加密货币交易所KuCoin及其创始人Chun Gan和Ke Tang&#xff0c;遭到美国南区纽约地区检察官办公室起诉&#xff0c;理由是KuCoin及其两位创始人违反了美国反洗钱规范和未…

Mysql的高级语句3

目录 一、子查询 注意&#xff1a;子语句可以与主语句所查询的表相同&#xff0c;但是也可以是不同表。 1、select in 1.1 相同表查询 1.2 多表查询 2、not in 取反&#xff0c;就是将子查询结果&#xff0c;进行取反处理 3、insert into in 4、update…