---恢复内容开始---

Floyd–Warshall(简称Floyd算法)是一种著名的解决任意两点间的最短路径(All Paris Shortest Paths,APSP)的算法。从表面上粗看,Floyd算法是一个非常简单的三重循环,而且纯粹的Floyd算法的循环体内的语句也十分简洁。我认为, 正是由于“Floyd算法是一种动态规划(Dynamic Programming)算法”的本质,才导致了Floyd算法如此精妙。因此,这里我将从Floyd算法的状态定义、动态转移方程以及滚动数组等重要方 面,来简单剖析一下图论中这一重要的基于动态规划的算法——Floyd算法。

在动态规划算法中,处于首要位置、且也是核心理念之一的就是状态的定义。在这里,把d[k][i][j]定义成:

“只能使用第1号到第k号点作为中间媒介时,点i到点j之间的最短路径长度。”

图中共有n个点,标号从1开始到n。因此,在这里,k可以认为是动态规划算法在进行时的一种层次,或者称为“松弛操作”。d[1][i][j]表示 只使用1号点作为中间媒介时,点i到点j之间的最短路径长度;d[2][i][j]表示使用1号点到2号点中的所有点作为中间媒介时,点i到点j之间的最 短路径长度;d[n-1][i][j]表示使用1号点到(n-1)号点中的所有点作为中间媒介时,点i到点j之间的最短路径长度d[n][i][j]表示 使用1号到n号点时,点i到点j之间的最短路径长度。有了状态的定义之后,就可以根据动态规划思想来构建动态转移方程。

动态转移的基本思想可以认为是建立起某一状态之前状态的一种转移表示。 按照前面的定义,d[k][i][j]是一种使用1号到k号点的状态,可以想办法把这个状态通过动态转移,规约到使用1号到(k-1)号的状态,即 d[k-1][i][j]。对于d[k][i][j](即使用1号到k号点中的所有点作为中间媒介时,i和j之间的最短路径),可以分为两种情况: (1)i到j的最短路不经过k;(2)i到j的最短路经过了k。不经过点k的最短路情况下,d[k][i][j]=d[k-1][i][j]。经过点k的 最短路情况下,d[k][i][j]=d[k-1][i][k]+d[k-1][k][j]。因此,综合上述两种情况,便可以得到Floyd算法的动态转 移方程:

d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])(k,i,j∈[1,n]

最后,d[n][i][j]就是所要求的图中所有的两点之间的最短路径的长度。在这里,需要注意上述动态转移方程的初始(边界)条件,即d[0] [i][j]=w(i, j),也就是说在不使用任何点的情况下(“松弛操作”的最初),两点之间最短路径的长度就是两点之间边的权值(若两点之间没有边,则权值为INF,且我比 较偏向在Floyd算法中把图用邻接矩阵的数据结构来表示,因为便于操作)。当然,还有d[i][i]=0i∈[1,n]

这样我们就可以编写出最为初步的Floyd算法代码:

void floyd_original() {
for(int i = ; i <= n; i++)
for(int j = ; j <= n; j++)
d[][i][j] = graph[i][j];
for(int k = ; k <= n; k++) {
for(int i = ; i <= n; i++) {
for(int j = ; j <= n; j++) {
d[k][i][j] = min(d[k-][i][j], d[k-][i][k] + d[k-][k][j]);
}
}
}
}

几乎所有介绍动态规划中最为著名的“0/1背包”问题的算法书籍中,都会进一步介绍利用滚动数组的技巧来进一步减少算法的空间复杂度,使得0/1背 包只需要使用一维数组就可以求得最优解。而在各种资料中,最为常见的Floyd算法也都是用了二维数组来表示状态。那么,在Floyd算法中,是如何运用 滚动数组的呢?

再次观察动态转移方程d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j]),可以发现每一个第k阶段的状态(d[k][i][j]),所依赖的都是前一阶段(即第k-1阶 段)的状态(如d[k-1][i][j],d[k-1][i][k]和d[k-1][k][j])。

上图描述了在前面最初试的Floyd算法中,计算状态d[k][i][j]时,d[k-1][][]和d[k][][]这两个二维数组的情况 (d[k-1][][]表示第k-1阶段时,图中两点之间最短路径长度的二维矩阵;d[k][][]表示第k阶段时,图中两点之间最短路径长度的二维矩 阵)。红色带有箭头的有向线段指示了规划方向。灰色表示已经算过的数组元素,白色代表还未算过的元素。由于d[k-1][][]和d[k][][]是两个 相互独立的二维数组,因此利用d[k-1][i][j],d[k-1][i][k]和d[k-1][k][j](皆处于上方的二维数组中)来计算d[k] [i][j]时没有任何问题。

那如何利用一个二维数组来实现滚动数组,以减小空间复杂度呢?

上图是使用滚动数组,在第k阶段,计算d[i][j]时的情况。此时,由于使用d[][]这个二维数组作为滚动数组,在各个阶段的计算中被重复使 用,因此数组中表示阶段的那一维也被取消了。在这图中,白色的格子,代表最新被计算过的元素(即第k阶段的新值),而灰色的格子中的元素值,其实保存的还 是上一阶段(即第k-1阶段)的旧值。因此,在新的d[i][j]还未被计算出来时,d[i][j]中保存的值其实就对应之前没有用滚动数组时d[k- 1][i][j]的值。此时,动态转移方程在隐藏掉阶段索引后就变为:

d[i][j] = min(d[i][j], d[i][k]+d[k][j])(k,i,j∈[1,n]

赋值号左侧d[i][j]就是我们要计算的第k阶段是i和j之间的最短路径长度。在这里,需要确保赋值号右侧的d[i][j], d[i][k]和d[k][j]的值是上一阶段(k-1阶段)的值。前面已经分析过了,在新的d[i][j]算出之前,d[i][j]元素保留的值的确就 是上一阶段的旧值。但至于d[i][k]和d[k][j]呢?我们无法确定这两个元素是落在白色区域(新值)还是灰色区域(旧值)。好在有这样一条重要的 性质,dp[k-1][i][k]和dp[k-1][k][j]是不会在第k阶段改变大小的。也就是说,凡是和k节点相连的边,在第k阶段的值都不会变。 如何简单证明呢?我们可以把j=k代入之前的d[k][i][j]=min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])方程中,即:

d[k][i][k]

= min(d[k-1][i][k], d[k-1][i][k]+d[k-1][k][k])

= min(d[k-1][i][k], d[k-1][i][k]+0)

= d[k-1][i][k]

也就是说在第k-1阶段和第k阶段,点i和点k之间的最短路径长度是不变的。相同可以证明,在这两个阶段中,点k和点j之间的的最短路径长度也是不 变的。因此,对于使用滚动数组的转移方程d[i][j] = min(d[i][j], d[i][k]+d[k][j])来说,赋值号右侧的d[i][j], d[i][k]和d[k][j]的值都是上一阶段(k-1阶段)的值,可以放心地被用来计算第k阶段时d[i][j]的值。

利用滚动数组改写后的Floyd算法代码如下:

void floyd() {
for(int k = ; k <= n; k++)
for(int i = ; i <= n; i++)
for(int j = ; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

因此,通过这篇文章的分析,我们可以发现,Floyd算法的的确确是一种典型的动态规划算法;理解Floyd算法,也可以帮助我们进一步理解动态规划思想。

转载 http://tech.artyoo.me/?p=81

http://www.cnblogs.com/chenying99/p/3932877.html

---恢复内容结束---

探求Floyd算法的动态规划本质(转)的更多相关文章

  1. 探求Floyd算法的动态规划本质

    Floyd–Warshall(简称Floyd算法)是一种著名的解决任意两点间的最短路径(All Paris Shortest Paths,APSP)的算法.从表面上粗看,Floyd算法是一个非常简单的 ...

  2. Floyd 算法的动态规划本质

    [转载自:http://www.cnblogs.com/chenying99/p/3932877.html] Floyd–Warshall(简称Floyd算法)是一种著名的解决任意两点间的最短路径(A ...

  3. [图论]Floyd 算法小结

    Floyd 算法小结  By Wine93 2013.11 1. Floyd算法简介 Floyd算法利用动态规划思想可以求出任意2点间的最短路径,时间复杂度为O(n^3),对于稠密图, 效率要高于执行 ...

  4. Floyd 算法求多源最短路径

    Floyd算法: Floyd算法用来找出每对顶点之间的最短距离,它对图的要求是,既可以是无向图也可以是有向图,边权可以为负,但是不能存在负环(可根据最小环的正负来判定). 基本算法: Floyd算法基 ...

  5. 图论篇3——最短路径 Dijkstra算法、Floyd算法

    最短路径 问题背景:地图上有很多个城市,已知各城市之间距离(或者是所需时间,后面都用距离了),一般问题无外乎就是以下几个: 从某城市到其余所有城市的最短距离[单源最短路径] 所有城市之间相互的最短距离 ...

  6. 多源最短路(floyd算法)

    Floyd算法: 如何简单方便的求出图中任意两点的最短路径 Floyd-Warshall算法(O(n)比较适用于边较多的稠密图(Dense Graph)) Floyd算法用来找出每对顶点之间的最短距离 ...

  7. 图论——Floyd算法拓展及其动规本质

    一.Floyd算法本质 首先,关于Floyd算法: Floyd-Warshall算法是一种在具有正或负边缘权重(但没有负周期)的加权图中找到最短路径的算法.算法的单个执行将找到所有顶点对之间的最短路径 ...

  8. 算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法

    一.名称 动态规划法应用 二.目的 1.掌握动态规划法的基本思想: 2.学会运用动态规划法解决实际设计应用中碰到的问题. 三.要求 1.基于动态规划法思想解决背包问题(递归或自底向上的实现均可): 2 ...

  9. 温故知新 —— Floyd算法

    什么?Floyd?sb O(n ^ 3) 算法早不用了,右上角红叉吧.我之前虽然也认识过 Floyd 算法的重要性,不过多少也是这么想的.然而最近三天连续 rand 到了好几道有关的题目,让我彻底重新 ...

随机推荐

  1. iOS_15_通过代码自己定义cell_微博UI

    终于效果图: BeyondTableViewController.h // // BeyondTableViewController.h // 15_代码自己定义cell_weibo // // Cr ...

  2. Android消息机制(1)

    在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易.更好地架构系统,避免一些低级的错误.在学习Android中消息机制之前,我 ...

  3. C#总结项目《影院售票系统》编写总结完结篇

    回顾:昨天总结了影院售票系统核心部分-售票,整个项目也就完成了2/3了,需求中也要求了对销售信息的保存,今天就继续总结销售信息的保存以及加载销售信息. 分析:退出程序时将已售出票集合中的对象循环写入到 ...

  4. spring 配置触发器 (类似于定时任务)

    为什么会看这个? 发现项目中有的service中的方法没有地方调用.经查,发现在web.xml中加载的spring的配置文件中配置了这个方法, 经查这种方式是触发器,会定时执行,只需要配置一下.可以设 ...

  5. 利用FTP将Linux文件备份到Windows

    windows:Windows Server 2008 linux: CentOS release 5.5 (Final)       首先在windows上安装好FTP,本人使用的是Windows ...

  6. HAOI 硬币购物

    试题描述: 现在一共有4种硬币,面值各不相同,分别为ci(i=1,2,3,4).某人去商店买东西,去了tot次,每次带di枚ci硬币,购买价值为si的货物.请问每次有多少种付款方法. 输入: 第一行包 ...

  7. Rational Rose 7.0的使用(转)

    1.Rose如何隐藏类的属性和操作? 右击类图,选择Options->Suppress Attributes/Suppress Operations 2.Rose中如何表示双向关联? 右击关联线 ...

  8. 使用ecshop电子商务系统的100个小问题

    1:如何修改网站"欢迎光临本店" 回答:languages\zh_cn\common.php文件中, $_LANG['welcome'] = '欢迎光临本店';将他修改成你需要的字 ...

  9. 转发——推荐一些国外高质量Java开发者的博客

    学习Java很不错的一篇博客,总结了很详尽的Java开发者博客. http://www.admin10000.com/document/3373.html 这些博客具有以下特点: 文章的可读性和有独创 ...

  10. Python3学习之二Django搭建

    严格来讲,这篇应该是前一篇 的续集吧,这也属于环境搭建:搭建一个Web开发环境. 1,官网下载最新的Django,当前最新的是1.8.2.所以我就下的这个版本,下载下来的是一个gz包Django-1. ...