最短路径有三种比较普遍的算法
1.bellman_ford可以计算带有负权值的单源最短路径,并且可以判断是否存在最短路径;
2.dij可以非常快速的计算正权值的单源最短路径(最好加上heap优化)
3.floyd可以快速计算点对之间的最短路径(基本可以说是非常好写又非常快速的,其他单源算法用于点对间都比较慢)
还有一种算法,是昨天我在搞ZOJ1857时接触到的,SPFA,可以说是非常迅速的算法了..比Dij+Heap快了好几个数量级(SPFA大概60ms,而Dij至少300ms)
SPFA其实是类似bellman_ford算法,bellman_ford的依据是如果有最短路径,那么将所有边(E)松弛V-1次之后,必然得到其最短路径(因为最短路径最长为V-1个边的和),而在松弛的时候,通常利用flag标识该次是否松弛过从而可以快速跳出进行优化.
但可惜的是,bellman_ford通常都会将全部边都进行松弛,甚至有的边根本不需要,也要进行多余的判断,为了解决这个问题,我们引入了队列,将所有需要松弛的点入队,仅仅松弛队列中的点,因此能很快的得到最短路径
可以入队的点:
1.源点.源点是最初我们手工入队的点,注意,此处应该把所有的源点全部入队,否则会产生有些点无法到达导致的错误情况
2.经过松弛的点.如果一个点经过了松弛,那么该点的临点都有可能需要进行松弛,所以必须将该点入队进行随后判断
SPFA的具体算法:
1.初始化队列,所有源点入队
2.如果队列为空,那么计算完毕(END)
3.否则,取队首元素,判断其邻接点是否需要松弛
4.需要松弛的话,则进行松弛,并对该点松弛次数+1,如果次数大于V-1,跳出,表明不存在最短路径(END)
5.否则,判断该邻接点是否在队列中,如果不在,则入队,继续取邻接点
6.邻接点全部取完,跳至步骤2
SPFA的优化有两个,SLF(Smallest Label First)比较好实现,当入队时比较入队点和队首元素大小,如果小,则从队首入队,否则正常入队
具体代码如下:
上述代码是从ZOJ1857中弄来的,可以看到,对于多个源点来讲,必须都事先入队.另,used是用来判断是否在队列中的.
SPFA有一个非常厉害的独特利用之处,就是类似本题的多源点最短路径,非常实用..可以借鉴(我错了..其实dij他们都可以的..)