文章目录
定义图有:n个点,m条边
单源最短路
边权为正
1.1、朴素Dijkstra O(n^2)
算法步骤
假设起点是st;已经确定到st最短距离的点在集合S中
- d i s [ s ] = 0 , ( i ≠ s ) d i s [ i ] = + ∞ dis[s] = 0, (i\neq s)dis[i] = +\infty dis[s]=0,(i=s)dis[i]=+∞
- for(i = 0; i < n; i ++ )
2.1. 不在S中,距离st最近的点x
2.2. 用x更新它连的所有点y(松弛操作):
if(dis[y] > dis[x] + w(x, j)) dis[j] = dis[x] + w(x, y)
2.3. x放入S
第二步循环内每次都能确定一个点x的最短距离,重复n次就能确定所有点的最短距离。
以算法导论的图为例:
a. 初始化dis[s]=0
b. 到s最近的就是s本身,更新s连的y和t,
S
=
{
s
}
S=\{s\}
S={s}
c. 到s最近的是y(
y
:
5
<
t
:
8
<
x
:
∞
=
z
:
∞
y:5 < t:8<x:\infty=z:\infty
y:5<t:8<x:∞=z:∞),更新y连的t、x、z,
S
=
{
s
,
y
}
S=\{s,y\}
S={s,y}
d. 到s最近的是z(
z
:
7
<
t
:
8
<
x
:
14
z:7 < t:8 < x:14
z:7<t:8<x:14),更新z连的x,
S
=
{
s
,
y
,
z
}
S=\{s,y,z\}
S={s,y,z}
e. 到s最近的是t(
t
:
8
<
x
:
13
t:8 < x:13
t:8<x:13),更新t连的x,
S
=
{
s
,
y
,
z
,
t
}
S=\{s,y,z,t\}
S={s,y,z,t}
f. 到s最近的是x(只剩x了),结束。
算法证明
粗略证明:
- 如果某个点(下图中的u)由算法得到的答案不是最短路,那在理论最短路径中,u肯定是连在“比u晚进S的点”(下图中的y)后面。
1.1. 因为如果是连在"比u先进入S的点"(下图中的x)后面,由归纳得x是最短路,x能松弛到u,算法得到得答案也是理论最短路径,矛盾。 - 而路径中y在u前面,边权又都是正的,算法是挑近的进S,y肯定会比u更早进S,矛盾。
- 因此不存在u,进S的点由算法得到的值就是实际最短路的值。
下面是严格证明:
假设u之前的点,有性质:对于所有加入S的点,在这个点加入S时,根据算法的步骤所确定的st到这个点的距离,都是最短路。
记:根据算法的步骤所确定的 s t st st到点 i i i的距离 = d i s [ i ] dis[i] dis[i]; s t st st到点 i i i的实际最短路= a n s [ i ] ans[i] ans[i]
那么性质可简写为:对于所有加入S的点 i i i,在 i i i加入S时,都有 d i s [ i ] = a n s [ i ] dis[i]=ans[i] dis[i]=ans[i]。
命题如下:点
u
u
u加入S时,
u
u
u是第一个使这个性质不成立的点,即
d
i
s
[
u
]
≠
a
n
s
[
u
]
dis[u] \neq ans[u]
dis[u]=ans[u]
利用反证法证明不存在这样的点 u u u:
- u u u和 s t st st不同:因为 s t st st是第一个进入集合 S S S的结点, d i s [ s t ] = 0 dis[st]=0 dis[st]=0,是最短路,性质成立。
- 此时 S S S不是空集: u u u加进来时至少有点 s t st st
- 此时已经存在某条从 s t st st到 u u u的路径:算法每次都是找距离 s t st st最近的点,如果不存在 s t st st到 u u u的路径,那么 d i s [ u ] = ∞ dis[u]=\infty dis[u]=∞, u u u不可能被加进来。
- 不妨设目前所有
s
t
st
st到
u
u
u的路径中,最短的路径是 st —p1—> x —> y —p2—> u,
x
x
x是
y
y
y的前一个点,
y
y
y是第一个不属于
S
S
S的点,因此
x
x
x在
S
S
S中。
ps. (p1和p2可能不包含任何边,可能 s t = x st=x st=x或 y = u y=u y=u,但 x x x和 y y y不同,p2可能重新进入 S S S) - d i s [ x ] = a n s [ x ] dis[x]=ans[x] dis[x]=ans[x]:因为 u u u是第一个使这个性质不成立的点,而 x x x在 u u u之前且已经进入 S S S中。
-
d
i
s
[
y
]
=
a
n
s
[
y
]
dis[y]=ans[y]
dis[y]=ans[y]:点
x
x
x进入
S
S
S时,进行松弛更新了
y
y
y,得到了
d
i
s
[
y
]
dis[y]
dis[y]。
而 y y y在 s t st st—> u u u的最短路上,如果还存在其他st—p3—>y的最短路,那么st —p3—> y —p2—> u,才是st—>u的最短路,这和第4步的假设矛盾。
因此st —p1—> x —> y就是st—>y的最短路径。 - a n s [ y ] ≤ a n s [ u ] ans[y]\leq ans[u] ans[y]≤ans[u]:因为 y y y在 u u u之前,且所有路径都是正的。
- a n s [ u ] ≤ d i s [ u ] ans[u]\leq dis[u] ans[u]≤dis[u]:算法估计的最短路只可能比真实的最短路长。
- d i s [ u ] ≤ d i s [ y ] dis[u]\leq dis[y] dis[u]≤dis[y]:u和y都不在S中,而算法每次都会从不在S中的点里面挑距离 s t st st最近的,也就是dis更小的,而此时 u u u正准备进 S S S,而 y y y是在之后才进 S S S。因此 d i s [ u ] ≤ d i s [ y ] dis[u]\leq dis[y] dis[u]≤dis[y]。
- 根据第7、8、9步,我们得到
a
n
s
[
y
]
≤
a
n
s
[
u
]
≤
d
i
s
[
u
]
≤
d
i
s
[
y
]
ans[y]\leq ans[u] \leq dis[u] \leq dis[y]
ans[y]≤ans[u]≤dis[u]≤dis[y]
由根据第6步的 d i s [ y ] = a n s [ y ] dis[y]=ans[y] dis[y]=ans[y],有 a n s [ y ] = a n s [ u ] = d i s [ u ] = d i s [ y ] ans[y] = ans[u] = dis[u] = dis[y] ans[y]=ans[u]=dis[u]=dis[y]
可以看到 a n s [ u ] = d i s [ u ] ans[u] = dis[u] ans[u]=dis[u]和命题矛盾 - 综上不存在这样的点 u u u,因此算法所求的所有点都满足性质,即进入S的点所确定的距离都是最短路。
算法实现
在这里插入代码片
1.2、堆优化Dijkstra O(mlogn)
边权为负
2.1、Bellman-Ford O(nm)
可以处理限制只经过k条边的最短路问题。