一、课程目标
- 路径
- 多源最短路径
- floyd算法
- 松弛设计
- 模拟过程
- 应用要点
二、目标详解
1、路径
路径指图的一组顶点组成的序列,按照路径的方向,相邻顶点在图上是邻接的。
如果顶点和边都不重复出现,则称为简单路径(simple path)。
如果起点和终点相同且没有其它重复的顶点和边,则称为环(cycle)。
2、多源最短路径
给定一个图,求其中任意两个点之间的最短路径,
3、floyd算法
floyd-warshall算法,是解决任意两点间最短路径的一种经典算法,可以正确处理有向图或负权图的最短路径问题,同时也可以被用于计算有向图的传递闭包。算法的时间复杂度为O(N^3),空间复杂度为O(N^2)。
分析任意顶点x到y的最短距离d(x, y),初始分为两种情况:
- x能邻接到y,d(x, y) = w(边权)
- x不邻接y,d(x, y) = oo(无穷大)
现在假设有一个顶点k,已经计算出d(x, k)和d(k, y),也即发现了一条新路径x -> k -> y,比较d(x, k) + d(k, y) < d(x, y),如果不等式成立就表示新的路径比原路径短,此时就可以更新d(x, y) = d(x, k) + d(k, y),这种做法称之为’松弛’。
”松弛“过程就是对两点间路径求最短距离的过程,如果x到y的所有中间顶点都参加了”松弛”,则最后运算的d(x, y)就是最短路径。
依次类推,遍历图中所有的顶点k,将其它点按照以上x和y的分类,对d(x, y)进行松弛,最后就得到了任意两个顶点之间的最短路径。
4、松弛设计
用一个n*n的矩阵d[N][N]存储任意两点之间的最短距离。
初始化:
- d[i][i] = 0
- d[i][j] = w(权),如果i邻接j
- d[i][j] = oo(无穷大),如果i不邻接j
循环每个顶点k,对每个经过k的路径“松弛”:
- 理解:d(x, k)即为矩阵第k列的每个元素, d(k, y)即为矩阵第k行的每个元素,
- 因此,循环第k列的每个元素d[i][k],循环第k行的每个元素d[k][j],两两相加对原最短路径进行松弛。
if(d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];
每个顶点都这样松弛过之后,矩阵的每个元素即为任意两点间的最短距离。
代码模板
:
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(nt j=1; j<=n; j++) {
if(d[i][k] + d[k][j] < d[i][j])
d[i][j] = d[i][k] + d[k][j];
}
5、模拟过程
最短路矩阵初始如下:
0 | 2 | oo | oo | 2 |
---|---|---|---|---|
oo | 0 | 4 | 4 | oo |
oo | oo | 0 | 6 | oo |
oo | oo | oo | 0 | 8 |
oo | 10 | oo | oo | 0 |
对顶点1,循环第一列的所有值 + 循环第一行的所有值,进行松弛判断:
if(d[i][k] + d[k][j] < d[i][j])
d[i][j] = d[i][k] + d[k][j];
顶点1松弛结束,矩阵没有变化,因为顶点1没有进来的路径。
然后用顶点2进行松弛,红色的是被松弛的点:
0 | 2 | 6 | 6 | 2 |
---|---|---|---|---|
oo | 0 | 4 | 4 | oo |
oo | oo | 0 | 6 | oo |
oo | oo | oo | 0 | 8 |
oo | 10 | 14 | 14 | 0 |
与原始矩阵对照,可以看出松弛的过程:
- 例如:d[1][3]原来不通,现在经过2中转,d[1][2] + d[2][3]=6<oo,于是d[1][3]->6
提示
:有一个技巧叫十字交叉法,顶点2对应第二行、第二列,其它元素的值与横纵对应元素的和进行比较。
依次松弛下去,最后得到:
0 | 2 | 6 | 6 | 2 |
---|---|---|---|---|
oo | 0 | 4 | 4 | 12 |
oo | 24 | 0 | 6 | 14 |
oo | 18 | 22 | 0 | 8 |
oo | 10 | 14 | 14 | 0 |
6、应用要点
回路
:
一般Floyd都应用与有向图,但也可用于无向图,但两者有一些区别。
核心在于回路,如果存在负回路,也即可以无限制走下去,每次路径都小于原路径,此时Floyd算法就会失真。
有向图的回路情况不多,而无向图肯定是回路,因此无向图基本要都是正边才好用此算法。
最大路
:
如果求最大路径,可把权设置为负的求最短路,最后再正回来。
路径
:
如果要输出路径,可用path[i][j]来表示i->j最短路上j的前趋点,最后反向输出。