Joern的华为软挑之路(二):SPFA算法

 

实际上我写这篇文章的时候我们的比赛之路已经结束了,无奈止步于64强之外,但是我们收获的东西却不少,在这里同各位分享一下我们做这个比赛的思路和遇到的歧路。

 

一、Bellman - ford算法

各位参与比赛的小伙伴都知道,费用流是解决这个问题的必不可少的一部分,在一开始我们并没有采用SPFA算法(大佬不要嘲笑),咱队的小伙伴对于算法这一块都是刚刚起步,通过网络我们首先搜索到了此算法。在这里简单介绍一下吧。

适用条件&范围:

单源最短路径(从源点s到其它所有顶点v);

有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);

边权可正可负(如有负权回路输出错误提示);

差分约束系统;

Bellman-Ford算法的流程如下:
给定图G(V, E)(其中VE分别为图G的顶点集与边集),源点s数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n], Distant[s]0

以下操作循环执行至多n-1次,n为顶点数:
对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;

为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。

可知,Bellman-Ford算法寻找单源最短路径的时间复杂度为O(V*E).

BellmanFord算法可以大致分为三个部分
第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
第二,进行循环,循环下标为从1n1n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
第三,遍历途中所有的边(edgeuv)),判断是否存在这样情况:
dv) > d (u) + w(u,v)
则返回false,表示途中存在从源点可达的权为负的回路。
 
之所以需要第三部分的原因,是因为,如果存在从源点可达的权为负的回路。则 应为无法收敛而导致不能求出最短路径。 

 

二、SPFA算法

一开始我们并没有发现ford算法的不足,因为case规模较小,ford算法实现较简单,多少个点就进行多少次,每次都遍历所有边进行松弛操作(就是取当前的边到达点路径的最小值,到达的路径更小就更新),最后必然求得单源单汇的最短路径。但是我们忽视了他的效率,第二批case一出现就不行了。于是我们上网查找了SPFA算法,其实也就是Ford算法的队列优化。

SPFA(Shortest Path Faster Algorithm) [图的存储方式为邻接表]是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。SPFA在形式上和BFS非常类似,不同的是BFS中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本

身被改进,于是再次用来改进其它的点,这样反复迭代下去。判断有无负环:如果某个点进入队列的次数超过V次则存在负环(SPFA无法处理带负环的图)。

SPFA算法有两个优化算法 SLF和 LLL:SLF:Small Label First策略,设要加入的节点是j,队首元素为i,若dist(j)x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。引用网上资料,SLF可使速度提高 15 ~ 20%;SLF + LLL可提高约 50%。

 

 

 

你可能感兴趣的:(各类竞赛)