基础最短路算法讲解

最短路问题是什么

给定一个有向带权图和两个点s,t,求一条路径从s到t,并且这条路径的边权和最小。这个问题称为最短路问题

最基础的操作——松弛

设dis[s][t]表示从s到t最短路的边权和,那么它一定满足一个性质:对于任意k,dis[s][t]<=dis[s][k]+dis[k][t],否则显然从s走到k再走到t更优。

如果dis[s][t]>dis[s][k]+dis[k][t],则把dis[s][t]更新为dis[s][k]+dis[k][t],这个操作称为用点k对s,t进行松弛。

松弛是所有最短路算法的基础。

算法1 Floyd

Floyd可以用来求任意两点的最短路。

假设我们已经求出了从s到t,经过的点只可能是1..k-1中的一些点,我们考虑把k加进去,也就是用k对s,t进行松弛。这样从s到t经过的点就可能是1..k中的点。

一开始如果s=t则dis[s][t]=0,否则如果s到t有边则dis[s][t]为边权否则为\infty∞

枚举k从1到n,s,t显然也要从1到n,这样就能求出任意两点间的最短路了。

注意k要在外层循环

时间复杂度稳定的O(n^3)O(n3)

代码只需要5行。(甚至可以更低)

算法2 Bellman-Ford

Bellman-Ford是用来求一个点s到所有点的最短路的。

如果从s到t有最短路,那么经过的边数一定可以<=n-1。

证明:假设边数可以>=n,那么路径中肯定经过了重复的点,那就出现了一个环。

如果环权>=0,显然可以把路径中这个环删掉。

否则?那么我们就找到了负权环。

(对于Floyd判断负权环,只需考虑是否有s满足dis[s][s]<0)

假设我们已经求出了经过从s经过不超过k-1条边到t的最短路。

然后对于每条边u,vu,v,可以直接定义dis[u][v]是这条边的边权(因为我们不需要算出u到v的最短路),对每条边松弛一次就可以了。

松弛完n-1次以后,如果还能松弛,那么就找到负权环了。

一个基本上没什么用的优化,在一次对每条边的松弛中,如果每次松弛都没更新,那就不用松弛了。

时间复杂度O(nm)O(nm)。

算法3 SPFA

当s到一个点的最短路更新了,这个点才用再去更新其他点。

我们用一个数据结构存准备拿来更新其它点的点。

每次从这里面取出一个,然后更新其它点,

如果一个点被更新了,就放进数据结构。

这就要分成两种情况(优先队列不考虑)

1. 数据结构是队列(通常情况)

这样就是BFS的版本。

在把一个点放进队列的时候,如果它已经在队列里面了,就不放了(qwq)

由于一个点第i次入队的时候(一开始s入队算第0次),从s到它最短路的长度至少是i,如果一个点入队了n次及以上,那么就出现了负权环。

时间复杂度不稳定,可以写作O(km)O(km),其中k是每个点的平均入队次数。

但是实际上,这个复杂度实际上是O(nm)O(nm)。不光是出现负权环的情况,正权图也是可以的。

具体的卡法:假设100000个点。

把点排成10行10000列,连成网格图,其中横边边权比较大且随机,竖边边权为1。

从左上开始跑最短路。

关于卡SPFA?不卡是情分,卡是本分。

连noi的赛场上 也是卡了SPFA

基础最短路算法讲解

2. 数据结构是栈

这个就是DFS的版本。

当然可以用系统栈。

当把一个点更新后,立刻用这个点开始更新其它点,如果从一个点更新到了它自己,那么显然出现了负权环。

复杂度实际上是指数级的。

卡法? 基础最短路算法讲解

以这样的图为基础让他从1到3往后走,然后到2,再到3,发现前面的路要重走一遍

就能卡到指数级了。

算法4 Dijkstra

Dijkstra可以用在边权非负的情况求出s到所有点的最短路。

我们对每个点标记它是否已经更新了其它点。

每次选出还没有用来更新其它点中目前最短路最小的点,拿来更新其它点。

这东西为什么正确呢?

当我选出一个点的时候,它已经被之前选出来的点更新过了,准备拿来更新其它点。

显然它不会再被另外的点更新了。

那么这个点就已经得到了最短路。

如果直接选择,复杂度为O(n^2)O(n2)。

优化

用优先队列存最短路和对应的点。

但是优先队列很难解决对最短路的更新。

方案1: 更新了再次扔进优先队列,新的一定会在原来的之间出队。 那原来的出队的时候,由于已经更新过这个点,就跳过。时间复杂度O(m \log m)O(mlogm)

方案2: 用支持修改元素值的优先队列 例如手写个什么左偏树之类的。时间复杂度O(m\log n)O(mlogn),也可以手写斐波那契堆/直接用pbds里的priority_queue,复杂度O(n\log n+m)O(nlogn+m)

上一篇:CF144D


下一篇:Windows安装jdk8