首先还是感谢 Alex_Wei 的博客 图论基础.
这篇博客可以理解成学习笔记之类的.所以记叙比较散乱是正常的.嗯.
一.最短路
以下内容,提最短路时若不做特殊说明,默认图是 无负环的存在负权有向图.
结论
- 最短路中不存在环.
若存在环,只能是负环.
- 单源最短路中,源点到图中任意一点的最短路最多有 n − 1 n-1 n−1 条边.
考虑 n − 1 n-1 n−1 条边构成图中可能存在的最小的链.若存在超过 n − 1 n-1 n−1 条边,则路径上有环.这与 结论 1 矛盾.
- 最短路树
对于每个点的最短路,考虑将它最短路上的前驱节点(若有若干,选择其中一个) v ( d i s u = d i s v + w ) v(dis_u=dis_v+w) v(disu=disv+w) 认作其父亲.则若所有点对源点都可达,这样会形成一种树形结构满足源点出发的任意一条路径都是最短路.我们叫他最短路树.
最短路树不止一种.若保留所有的
算法
SPFA
SPFA 是 Bellman-Fold 算法的队列优化.
其核心思想是对全点做
n
−
1
n-1
n−1 轮松弛.即遍历边
e
(
u
→
v
,
w
)
e(u \rightarrow v, w)
e(u→v,w),检查若
d
i
s
v
>
d
i
s
u
+
w
dis_v>dis_u+w
disv>disu+w,令
d
i
s
v
←
d
i
s
u
+
w
dis_v \leftarrow dis_u+w
disv←disu+w.
松弛
i
i
i 轮后,
d
i
s
u
dis_u
disu 表示源点到
u
u
u 经过边数不超过
i
i
i 的最短路长度.
SPFA 判断负环
初始时,将全点丢入队列, d i s dis dis 赋为 0 0 0.执行 SPFA 并更新每个点的 入队次数,若发现某点入队次数 ≥ \ge ≥ n − 1 n-1 n−1,则原图存在负环.
入队次数,其实等于最短路经过的边数.当第 x x x 次入队时,一定有路径边数 ≥ x \ge x ≥x.否则它肯定会在之前的轮次更新.
差分约束系统
考虑一组变元
(
x
1
,
x
2
,
x
3
,
.
.
.
,
x
n
)
(x_1, x_2, x_3, ..., x_n)
(x1,x2,x3,...,xn) 的取值:
若存在若干组限制
x
i
+
d
≥
x
j
x_i+d \ge x_j
xi+d≥xj.如何构造满足所有限制的解.
发现
x
i
+
d
≥
x
j
x_i+d \ge x_j
xi+d≥xj 形如最短路里的三角形不等式:
对于任意一种单元最短路,若存在边
e
(
u
→
v
,
w
)
e(u \rightarrow v, w)
e(u→v,w),一定有
d
i
s
u
+
w
≥
d
i
s
v
dis_u+w \ge dis_v
disu+w≥disv.
于是我们对于每组限制,建边 e ( x i → x j , d ) e(x_i \rightarrow x_j, d) e(xi→xj,d),然后进行最短路算法即可.
为了防止存在单元最短路无法扩展到的点,通常,我们会建立超级源点
S
S
S,并对所有点建边
e
(
S
→
u
,
0
)
e(S \rightarrow u, 0)
e(S→u,0).
这显然不会影响答案的正确性.只是限制了求解出的
x
i
x_i
xi 小于等于给
S
S
S 赋值的初始距离
d
i
s
S
dis_S
disS.
考虑最短路求得解的特殊性:
最短路求得差分约束的解满足为所有
x
i
≤
d
i
s
S
x_i \le dis_S
xi≤disS 的最大解.
考察差分约束建模后产生的最短路树,若将任意某些 x i + 1 x_i+1 xi+1,则必定存在其某个 x i x_i xi 最短路树上的父亲与 x i x_i xi 不满足三角形不等式.
同理,最长路求得的解是满足所有 x i ≥ d i s S x_i \ge dis_S xi≥disS 的最小解.
Dijkstra
适用于非负权图最短路.
每次选择距离最短的节点,将其标记为已经求解了最短路的节点,然后用其更新其他没有标记的节点.
在非负权图中,对于一种最短路树,可以保证其父亲的距离一定小于等于自身的距离.归纳法即可证明 Dijkstra 算法的正确性.
值得注意,使用堆优化的 Dijkstra 的算法时间复杂度为 O ( m log m ) O(m\log m) O(mlogm).当 m m m 接近 n 2 n^2 n2 时,使用不加堆优化的 Dijkstra 算法更优,复杂度为 O ( n 2 ) O(n^2) O(n2).
01 Bfs
是 Dijkstra 算法在特殊图上的一种变式.
考虑一张图只有
0
/
1
0/1
0/1 两种边权.我们使用 deque 进行 Bfs.每次取出队头,若碰到
0
0
0 边,将新节点放入对头;若碰到
1
1
1 边,将新节点放入队尾.
结论:第一次被更新时的距离即是每个节点的最短距离.
目前没有找到比较好的证明.
168万+





