1
最短路& 最小生成树
2
Part 1
最短路
3
问题的提出
给定一个图 G(V,E) , V 是顶点集合, E 是边集合,每一条边有一个权值 c ,给定一个起始点 S 和终止点 D ,求从 S 出发走到 D 的权值最小路径,即为最短路径
4
问题的类型单源最短路所有点对最短路
单位权值非负权值负权值
5
单源最短路径Single Source Shortest Path
求给定点到其他所有点的最短路径
问题描述:
相关算法:Dijkstra
Bellman-Ford
SPFA( 队列优化的 Bellman-Ford)
6
迷宫问题
典型的搜索问题,直接 BFS 即可其实也可看做单源最短路问题
起点终点对应
迷宫的每个格子对应图中的点每种移动对应图中的边,且边权值为 1
对应关系:
思考:边权不为 1 的情况能 BFS 解决吗?为什么?
7
一个例子
求图中从 A 到 C 的最短路思考:为什么 BFS 不行?
因为 dis[c]>dis[b]+len(b,c)dis[c]=dis[b]+len(b,c)
怎么解决?
松弛操作
8
Dijkstra算法设置一顶点集合 S ,从源点 s 到集合中
的顶点的最终路径的权值均已确定
对于不在 S 中的点来说,有一个距离的估计值,初始时为正无穷,会随松弛操作而改变
反复选取不在 S 中且估计值最小的点 u ,将其加入 S ,并用它去松弛其他点
9
Dijkstra算法时间复杂度? O(V^2)
算法复杂度的瓶颈? 如何快速找最小值
堆 OR 优先队列!时间复杂度? O(ElogV)
Sea Base Exploration ( CDOJ 1485 )状态压缩 + 优先队列的 BFS
其实就是用 dijkstra 解决的最短路!
10
负边权
如果负边只存在于源点 s 连出去的边,dijkstra 还是可以做的
dijkstra 能处理负边权么?不能! 1
2
310
20
-15
41事实上有图有真相
那么,对于一般的有负边权的呢?
11
Bellman-Ford算法•先来看伪代码:
bool Bellman-Ford(int s){for(i = 1; i <= v; ++i) d[i] = INF; 所有点的 d[] 值置为无
穷大d[s] = 0; 源点 d[] 值为 0for(i = 1; I < v; ++i)
for each edge(u, v, e)relax(u, v, w); 进行 v – 1 次松弛操作
for each edge(u, v, e)if(d[v] > d[u] + e) return false;
如果松弛操作结束后仍有可继续松弛的边,则图中存在负环return true;
}
12
Bellman-Ford算法正确性的证明
•如果原图中不存在负环:从 s 可达的任意点 v 的最短路至多经过 v – 1 条边。
对于这 v – 1 条边,第一次松弛后第一条边不可再松弛。第二次松弛后第二条边不可再松弛,…这样便证明了算法在没有负环的图上的正确性。
•如果原图中存在负环: 显然对于负环中的任何一个点,它的最短路会是负无穷,所以松弛操作不会停止。算法返回 false 。
13
Bellman-Ford算法时间复杂度
两个 for 循环 O ( VE )相当慢!做了很多无用功!一个优化:每次只用距离减小的点去更新其他点如何实现? 队列!
14
Bellman-Ford算法整理一下新算法的思路:
Step 2: 当队列不为空时每次从队列中取出队首,对队首的每条边进行松弛。将松弛后d[] 值改变并且不在队列中的点加入队列。
Step 1: 初始时所有点 d[] 值置 INF ,源点d[] 为 0 。将源点放进队列。
这就是 SPFA 算法!
15
SPFA算法两点说明:
时间复杂度 最坏 : O(VE)
一般 : O(kE)
如何判负环?对每个点记录一个 num 值,表示被更新了多少次,如果某个点被更新的次数超过 n-1 次,则有负环
16
所有点对最短路
求所有点到其他点的最短路径
问题描述:
相关算法:V 次 dijkstra O(V^3) or
O(EVlogV)V 次Bellman_Ford
O(V^2E)
V 次 SPFA O(kVE)
1 次 floyd! O(V^3)
17
Floyd算法先看代码:
for(k=1;k<=V;k++)for(i=1;i<=V;i++)for(j=1;j<=V;j++)if(dis[i][j]>dis[i][k]+dis[k][j])dis[i][j]=dis[i][k]+dis[k][j];
实际上是一个精巧的 DP !
18
Floyd算法DP 过程:
设 dis[i][j][k] 表示从 i 到 j 的路径中,经过的点的编号不超过 k的最短路边界条件: dis[i][j][0] = dis[i][j]
转移方程 :dis[i][j][k] = Min(dis[i][j][k-1] , dis[i][k][k-1] + dis[k][j][k-1]) (k > 0 , 0 <= i , j <= n)答案: dis[i][j][n]
19
Floyd算法时间复杂度: 三重 for 循环
O(V^3)原程序实际上是这个 DP 的一个精巧的实现,省略了最后一维数组
因为在第 k次进行更新时,只会用dis[u][k], dis[k][u] 对别的 dis 值进行更新,而 dis[u][k] 和 dis[k][u] 不会改变
20
最短路算法的应用求无向图最小环最小环是指一个图中一个至少包含三个顶点的边权和最小的环 Floyd !设某环编号最大的顶点为 L 另两个顶点编号分别为 M 和 N (M,N < L) ,则最大编号为 L 的最小环长度即为 mp(M,L) + mp(N,L) + dp(M,N) ,其中 dp(M,N) 表示以 0…L-1 号顶点为中间点时的最短路径,刚好符合 Floyd 算法最外层循环到 k=L 时的情况。
若为有向图呢? (CDOJ 1329)
21
最短路算法的应用求下面不等式组的一组解:
X1 - X2 <= 0X1 - X5 <= -1X2 - X5 <= 1X3 - X1 <= 5X4 - X1 <= 4X4 - X3 <= -1X5 - X3 <= -3X5 - X4 <= -3
Xi<=0(i=1,2,3,4,5)
求完最短路后图中每条边dis(v) <= dis(u) + w(u, v)对于不等式 Xi - Xj <= c ,把它化成不等式: Xi <= Xj + c ,就可以化成边 Vj -> Vi ,权值为c 。最后,我们在这张图上求一次单源最短路径,这些不等式就会全部都满足
22
最短路算法的应用求下面不等式组的一组解:
X1 - X2 <= 0X1 - X5 <= -1X2 - X5 <= 1X3 - X1 <= 5X4 - X1 <= 4X4 - X3 <= -1X5 - X3 <= -3X5 - X4 <= -3
Xi<=0(i=1,2,3,4,5)
对于最后一个 Xi<=0 怎么办?新添一个 X0=0, 并使 Xi-X0<=0!最后以 X0 为源点,跑最短路最后每个点的 dis 值就为答案注意:由于有负边权,不能 使用dijkstra
23
最短路算法的应用最后的图及答案:
X1=-5
X2=-3
X3=0
X4=-1
X5=-4
24
Part 2
最小生成树
25
最小生成树
两种常用算法:Prim O ( V^2 )Kruskal O(ElogE)
问题描述:在一个具有几个顶点的连通图 G 中,如果存在子图 G‘包含G 中所有顶点和一部分边,且不形成回路,则称 G’ 为图 G 的生成树,权值和最小的生成树则称为最小生成树
26
Prim算法基本思想:将生成树中的点一个一个加进来具体过程 :
设图 G = ( V , E ),其生成树的顶点集合为U。1 : 把 v0 放入 U
2 : 在所有 u∈U , v∈V-U 的边(u , v)∈E 中找一条最小权值的边,加入生成树。3 : 把 2 找到的边的 v 加入 U集合。如果 U集合已有 n个元素,则结束,否则继续执行2
27
Kruskal算法基本思想:贪心地将边按边权从小到大加进来具体过程:1 : 将边按边权从小到大排序2 :从剩下的边中选择一条不会产生环路的具有最小权值的边加入已选择的边的集合中 3 : 若已选 n-1 条边,则已选择的边的集合构成最小生成树,否则转 2
如何判环路? 并查集!
28
Thank You!