最短路学习

来源:互联网 发布:鸿合电子白板软件下载 编辑:程序博客网 时间:2024/06/11 23:50

 

dijkstra bellmanford spfa floyd的区别:

 

bellman-ford

 

可以有负权,但不能有负权回路,spfa是bellman-ford的队列优化,时间发咋度o(ke),其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

 

dijkstra

 

不可以有负权,但效率比bellman-ford快,o(2n次方),用二叉堆优化o((m+n)log n),斐波纳契堆能稍微提高一些性能,让算法运行时间达到o(m + n log n)。

 

floyd

 

算每对顶点之间的最短路,前几个是单源的

 

 

 

 

 

 

 

 

 

 

单源最短路径:Dijkstra算法



Dijkstra算法当中将节点分为已求得最短路径的集合(记为S)和未确定最短路径的个集合(记为U),
归入S集合的节点的最短路径及其长度不再变更,如果边上的权值允许为负值,那么有可能出现当与S
内某点(记为a)以负边相连的点(记为b)确定其最短路径时,它的最短路径长度加上这条负边的权值
结果小于a原先确定的最短路径长度,而此时a在Dijkstra算法下是无法更新的,由此便可能得不到正确的结果。
求带负权值边的单源最短路径可以用贝尔曼-福特算法。

 

 

1.初始化3个数组mp visit dist

2.n(n+n)次循环 

                         内层2个循环

                                        先找到当前dist内的最小值 ,记录下标k  ,visit[ k ]标记为1

                                        然后把k代入三角不等式dist[j]>dist[k]+mp[k][j] 更新dist 

 

单源最短路径:Bellman-Ford算法


用d[v]表示源点到每一个顶点的距离,运用松弛技术,对每个顶点v,逐步减小从源s到v的最短路径的权的估计值dist[v],
直到其达到实际最短路径的权.如果图中不包含从源点可达的负权回路,则函数返回true,否则返回false.
应用范围:
解决带负权的单源最短路径问题,也常用于查分约束系统问题的求解.

 

 

 

 

 

 

bellman-ford 的两种优化

 

1.分析 Bellman-Ford算法,不难看出,外层循环(迭代次数)|v|-1实际上取得是上限。由上面对算法正确性的证明可知,需要的迭代遍数等于最短路径树的高度。如果不存在负权回路,平均情况下的最短路径树的高度应该远远小于 |v|-1,在此情况下,多余最短路径树高的迭代遍数就是时间上的浪费,由此,可以依次来实施优化。

从细节上分析,如果在某一遍迭代中,算法描述中第7行的松弛操作未执行,说明该遍迭代所有的边都没有被松弛。可以证明(怎么证明?):至此后,边集中所有的边都不需要再被松弛,从而可以提前结束迭代过程。这样,优化的措施就非常简单了。

设定一个布尔型标志变量 relaxed,初值为false。在内层循环中,仅当有边被成功松弛时,将 relaxed设置为true。如果没有边被松弛,则提前结束外层循环。这一改进可以极大的减少外层循环的迭代次数。优化后的 bellman-ford函数如下。

 

function bellmanford(s:longint):boolean;

begin

        for i:=1 to nv do

             d[i]:=max;

        d[s]:=0;

        for i:=1 to nv-1 do

            begin

                  relaxed:=false;

                  for j:=1 TO ne do

                        if(d[edges[j].s]<>max) and (d[edges[j].e]>d[edges[j].s]+edges[j].w)

                        then

                                begin

                                         d[edges[j].e]:=d[edges[j].s]+edges[j].w ;

                                         relaxed:=true;

                               end;

                       if not relaxed then break;

            end;

        for i:=1 to ne do

                if d[edges[j].e]>d[edges[j].s]+edges[j].w then exit(false);

                    exit(true);

end;

 

 

 

 

2.SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。当队列不为空时,取出对首顶点,对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实现,需要用到一个先进先出的队列 queue 和一个指示顶点是否在队列中的标记数组 mark。为了方便查找某个顶点的邻接点,图采用临界表存储。

程序存储在 spfa.pas中。以usaco 3.2.6试题2为例。用邻接表写的程序。

需要注意的是:仅当图不存在负权回路时,SPFA能正常工作。如果图存在负权回路,由于负权回路上的顶点无法收敛,总有顶点在入队和出队往返,队列无法为空,这种情况下SPFA无法正常结束。

判断负权回路的方案很多,世间流传最广的是记录每个结点进队次数,超过|V|次表示有负权

还有一种方法为记录这个结点在路径中处于的位置,ord[i],每次更新的时候ord[i]=ord[x]+1,若超过|V|则表示有负圈

 

 

SPFA时间效率要优于第一种优化形式,但第一种优化形式的编码复杂度低于SPFA。两种优化形式的编程复杂度都低于Dijkstra算法。如果在判断是否存在负权回路,推荐使用第一种优化形式,否则推荐使用SPFA

 

 

dijkstra和spfa实现最短路hdu2544

 

Dijkstra+heap优化……