Johnson算法学习笔记

时间:2023-03-09 02:13:39
Johnson算法学习笔记

\(Johnson\)算法学习笔记。

在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法。这些算法可以快速的求出单源最短路,即一个源点的最短路.

而\(Floyd\)算法,这个及其简短的算法,可以以\(O(n^3)\)的复杂度算出任意一对点之间的最短路。

我们发现,\(floyd\)算法的时间复杂度和边的数量没有多大的关系,也就是说,\(floyd\)使用的最优条件是稠密图。

那么问题来了,如果我们面对一个点数非常多但是边数较少的图,我们该用什么算法呢?


\(johnson\)出现了。

\(johnson\)算法是一类用来处理多源最短路的算法,它的复杂度是\(O(n*m*log_n)\)。

简单的来说,\(johnson\)算法是糅合的两大单源最短路算法的算法。

\(dijkstra\)算法在算法界是一个公认非常好用的算法,只可惜其限制条件过多,必须没用负边才可以使用。

而\(SPFA\)就没有那么多限制了,它只用保证数据中不会出现负环即可,可是由于\(spfa\)算法及其不稳定,及其容易被卡成\(O(n*m)\)的复杂度。

\(johnson\)算法就利用了两大算法的优点。


首先\(johnson\)先利用\(SPFA\)将所有的边处理一下,将负边权全都转成正边权。

然后再每个点暴力跑\(dijkatra\)求出最短路。

第二步利用\(dijkstra\)跑最短路是十分显然好懂的,问题就是第一步将负边改为正边。

我们知道,直接将所有的负边加上一个极大值是错误的,我们要给所有的边加上一个合适的值。

那么这个值是什么呢?

我们先增加一个超级源,把所有点和它相连即可。

然后,我们来以超级源为源点跑一遍\(spfa\)。

然后我们对于每一条边加上\(spaf\)跑完后的\(dis[0][u]-dis[0][v]\)。

最后,把所有的\(dis[u][v]\)减去\(dis[0][u]-dis[u][v]\)还原。


让我们来证明其正确性:

1.边权不为负数。

由于\(dis[0][u]+w(u,v)>=dis[0][j]\);

所以必有\(dis[0][u]-dis[0][j]+w(u,v)>=0\)。

所以边权必定大于等于\(0\),可以用\(dijkstra\)跑。

2.还原的正确性。

我们有一条集合为\(\{A_1,A_2,A_3,,,,A_p\}\)的最短路。

我们对边权进行修改后,最短路改变的值为:\(dis[0][A_1]-dis[0][A_2]+dis[0][A_2]-...-dis[0][A_p]\)。

即:\(dis[0][A_1]-dis[0][A_p]\)。

所以,当我们修改一些权值时,任意两点之间的最短路改变的值是一个定值,即\(dis[0][u]-dis[v]\),

在最后的\(dis[u][v]\)上减掉即可。