最短路
定义
最短路问题是图论理论的一个经典问题。寻找最短路径就是在指定网络中两结点间找一条距离最小的路。最短路不仅仅指一般地理意义上的距离最短,还可以引申到其它的度量,如时间、费用、线路容量等。
——百度百科
名词解释
Chain · 链
:一个点和边的交错序列 $v_0 - e_1 - v_1 - e_2 - v_2 - \cdots - e_k - v_k$Trail · 迹
:对于一条路径 $w$,若$e_1, e_2, \cdots, e_k$ 两两互不相同,则 $w$ 是一条迹Path · 路径
:对于一条迹 $w$,除了 $v_0$ 和 $v_k$ 允许相同外,其余点两两互不相同,则称 $w$ 是一条路径Circuit · 回路
:对于一个迹 $w$,若 $v_0 = v_k$,则称 $w$ 是一个回路Cycle · 环
:对于一条路径 $w$,若 $v_0 = v_k$,则称 $w$ 是一个环
分类
单源最短路
包括确定起点的最短路径问题,确定终点的最短路径问题(与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。) 。
多源最短路
链式前向星
1 |
|
Dijkstra · 单源最短路
思路
- 将所有节点分成两个集合,已求出最短路的集合(集合1)和未求出最短路的集合(集合2)。
- 更新并记录集合2 中所有节点和源点的距离
- 从集合2 中找到距离源点距离最近的点
- 将该点移到集合1 中
- 重复步骤 2-3,直到集合2 为空
这个方法用了贪心的思想,每次把距离最小的点视作确定的,因为它已经是未确定的点中距离源点最近的了,不可能存在一条路经过其他未确定的点到这个点,距离还比直接到这个点近的了。
- 不能有负权边
模板
dis[]
:每个点到源点的距离vis[]
:每个点属于的集合,0 - 未确定,1 - 已确定
1 |
|
复杂度 · $O(n2)$
拓展
从一个点出发到所有点的距离:正 Dijkstra
从所有点出发到一个点的距离(POJ 3268 · Silver Cow Party)
把图反向
1
2
3int tmp = map[i][j];
map[i][j] = map[j][i];
map[j][i] = tmp;Dijkstra
优化
Floyd · 多源最短路
思路
我们定义一个数组 dis[k][x][y]
,表示只允许经过结点 $V_1$ 到 $V_k$,结点 $x$ 到结点 $y$ 的最短路长度。很显然, dis[n][x][y]
就是最终结点 $x$ 到结点 $y$ 的最短路长度。
dis[0][x][y]
是 $x$ 与 $y$ 的边权,或者 $0$,或者 $\infty$(当 $x$ 与 $y$ 间有直接相连的边的时候,为它们的边权;当 $x = y$ 的时候为零,因为到本身的距离为零;当 $x$ 与 $y$ 没有直接相连的边的时候,为 $\infty$)
dis[k][x][y] = min(dis[k-1][x][y], dis[k-1][x][k]+dis[k-1][k][y])
( dis[k-1][x][y]
为不经过 $k$ 点的最短路径,而 dis[k-1][x][k]+dis[k-1][k][y]
为经过了 $k$ 点的最短路)。
能有负权边
不能有负环
模板
1 | void Floyd() |
不难看出,$k$ 每次循环后只是调用了 $k - 1$ 的数据,而 $k$ 之前的数据对结果没有作用,因此第一维是可以省略的(数据可以不保存,直接在下一次循环被覆盖,但是还是需要有这 $k$ 次循环)
1 |
|
复杂度
- 时间复杂度:$O(n^3)$
- 空间复杂度:$O(n2)$
Bellman-Ford · 单源最短路
思路
松弛
每次松弛操作实际上是对相邻节点的访问,第 $n$ 次松弛操作保证了所有深度为 $n$ 的路径最短。由于图的最短路径最长不会经过超过 $|V| - 1$ 条边,所以可知贝尔曼-福特算法所得为最短路径。
负边权操作
与 Dijkstra 算法不同的是,迪科斯彻算法的基本操作“拓展”是在深度上寻路,而“松弛”操作则是在广度上寻路,这就确定了贝尔曼-福特算法可以对负边进行操作而不会影响结果。
负权环判定
因为负权环可以无限制的降低总花费,所以如果发现第 $n$ 次操作仍可降低花销,就一定存在负权环。
能有负权边
能有负环
优化
循环的提前跳出
在实际操作中,贝尔曼-福特算法经常会在未达到 $|V| - 1$ 次前就出解,$|V| - 1$ 其实是最大值。于是可以在循环中设置判定,在某次循环不再进行松弛时,直接退出循环,进行负权环判定。
最短路径快速算法
松弛操作必定只会发生在最短路径前导节点松弛成功过的节点上,用一个队列记录松弛过的节点,可以避免了冗余计算。该算法的复杂度为 $O(k|E|)$,$k$ 是个比较小的系数,但该结论未得到广泛认可。
模板
SPFA已死
1 |
|
复杂度 · $O(|V| · |E|)$
习题
A 最短路 · HDU 2544
B Til the Cows Come Home · POJ 2387
C Silver Cow Party · POJ 3268
D Heavy Transportation · POJ 1797
E Cow Contest · POJ 3660
F Edge Deletion · CodeForces 1076D
About this Post
This post is written by OwlllOvO, licensed under CC BY-NC 4.0.