1、求单源最短路SPFA算法求单源最短路-SPFA算法求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。 SPFA算法是西南交通大学段凡丁于1994年发表的.从名字我们就可以看出,这种算法在效率上一定有过人之处。 很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。 我们用数组d记录每个结点的最短
2、路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。 定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。 证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值dv变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,
3、算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证毕) 期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。实现方法:建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图) 编辑本段
4、伪代码SPFA实际上是Bellman-Ford基础上的优化SPFA(G,w,s) 1. INITIALIZE-SINGLE-SOURCE(G,s) 2. INITIALIZE-QUEUE(Q) 3. ENQUEUE(Q,s) 4. While Not EMPTY(Q) 5. Do u-DLQUEUE(Q) 6. For 每条边(u,v) in EG 7. Do tmp-dv 8. Relax(u,v,w) 9. If (dv tmp) and (v不在Q中) 10. ENQUEUE(Q,v) 一种更容易读懂的伪代码Procedure SPFA;Begininitialize-single-so
5、urce(G,s);initialize-queue(Q);enqueue(Q,s);while not empty(Q) do beginu:=dequeue(Q);for each vadju do begintmp:=dv;relax(u,v);if (tmpdv) and (not v in Q) then enqueue(Q,v);end;end;End;期望的时间复杂度O(2e) 对spfa的一个很直观的理解就是由无权图的bfs转化而来.在无权图中,bfs首先到达的顶点所经历的路径一定是最短路(也就是经过的最少顶点数).所以此时利用visitu,可以使每个顶点只进队一次.在带权图中
6、,最先到达的顶点所计算出来的路径不一定是最短路.一个解决方法是放弃visit数组,此时所需时间自然就是指数级的.所以我们不能放弃visit数组,而是在处理一个已经在队列中且当前所得的路径比原来更好的顶点时,直接更新最优解. 编辑本段标准SPFA过程(以求某个结点t到某个结点s的最短路为例,稍加修改即为单源最短路)program spfaprg; Pascal代码const maxp=10000; 最大结点数var 变量定义p,c,s,t:longint; p,结点数;c,边数;s:起点;t:终点a,b:array1.maxp,0.maxp of longint; ax,y存x,y之间边的权;b
7、x,c存与x相连的第c个边的另一个结点yd:array1.maxp of integer; 队列v:array1.maxp of boolean; 是否入队的标记dist:array1.maxp of longint; 到起点的最短路head,tail:longint; 队首/队尾指针procedure init;vari,x,y,z:longint;beginread(p,c); for i := 1 to c dobeginreadln(x,y,z); x,y:一条边的两个结点;z:这条边的权值inc(bx,0); bx,bx,0 := y; ax,y := z; bx,0:以x为一个结点
8、的边的条数inc(by,0); by,by,0 := x; ay,x := z;end;readln(s,t); 读入起点与终点end;procedure spfa(s:longint); SPFAvari,j,now,sum:longint;beginfillchar(d,sizeof(d),0);fillchar(v,sizeof(v),false);for j := 1 to p do dist j :=maxlongint;dists := 0; vs := true; d1 := s; 队列的初始状态,s为起点head := 1; tail := 1;while headdistno
9、w+anow,bnow,i thenbegindistbnow,i:= distnow+anow,bnow,i; 修改最短路if not vbnow,i then 扩展结点入队begininc(tail);dtail := bnow,i;vbnow,i := true;end;end;vnow := false; 释放结点inc(head); 出队end;end;procedure print;beginwriteln(distt);end;begininit;spfa(s);print;end. C语言代码#include#define maxint 2139062143int a10110
10、1,dist101,n;void spfa(int s)int q101,v101,h=0,t=1,x,i;/q为队列,v为Boolean数组,表示结点是否在队列中,h为头指针,t为尾指针memset(q,0,sizeof(q);memset(v,0,sizeof(v);memset(dist,127,sizeof(dist);/置dist数组为maxint(+),下同dists=0;qt=s;vs=1;while(ht)h=(h+1)%n;x=qh;vx=0;for(i=1;idistx+axi)disti=distx+axi;if(!vi)t=(t+1)%n;qt=i;vi=1;int m
11、ain()int m,s,t,i;scanf(%d%d,&n,&m);scanf(%d%d,&s,&t);memset(a,127,sizeof(a);for(i=1;i=m;i+)int x,y,z;scanf(%d%d%d,&x,&y,&z);axy=z;ayx=z;spfa(s);printf(%d,distt);system(pause);return 0; 编辑本段SPFA的各种实现 最基本的前项性优化的spfa(有向图)复杂度O(ke).vara,b,e:array1.1000 of longint;vis:array1.2000 of boolean;q,d,f:array1.2
12、001 of longint;n,m,i,s,t:longint;procedure qsort(l,r:longint);var i,j,x,y:longint;begini:=l;j:=r;x:=a(l+r) shr 1;repeatwhile aix do dec(j);if not(ij) then beginy:=ai; ai:=aj; aj:=y;y:=bi; bi:=bj; bj:=y;y:=ei; ei:=ej; ej:=y;inc(i);dec(j);end;until ij;if ir then qsort(i,r);if lj then qsort(l,j);end;pr
13、ocedure spfa(s:longint);var i,k,l,t:longint;beginfillchar(vis,sizeof(vis),0);for i:=1 to n do di:=maxlongint;ds:=0;l:=0;t:=1;q1:=s;viss:=true;repeatl:=l mod 10000 +1;k:=ql;for i:=fk to fk+1-1 doif dk+eidbi thenbegindbi:=dk+ei;if not visbi then begint:=t mod 10000 +1;qt:=bi;visbi:=true;end;end;visk:=
14、false;until l=t;end;Beginreadln(n,m);for i:=1 to m doreadln(ai,bi,ei);qsort(1,m);for i:=1 to m doif fai=0 then fai:=i;fn+1:=m+1;for i:=n downto 1 doif fi=0 then fi:=fi+1;readln(s,t);spfa(s);writeln(dt);end. 堆排实现(无向图)const MM=1000;vara,b,e:array1.MM of longint;q,d,f:array1.2*MM+1 of longint;vis:array
15、1.MM of boolean;n,m,i,s,t:longint;mh:longint;procedure heapify(i:longint);var l,r,max,temp:longint;beginl:=2*i;r:=l+1;max:=i;if (lamax) then max:=l;if (ramax) then max:=r;if maxi then begintemp:=ai; ai:=amax; amax:=temp;temp:=bi; bi:=bmax; bmax:=temp;temp:=ei; ei:=emax; emax:=temp;heapify(max);end;e
16、nd;procedure heap;var i,temp:longint;beginmh:=m;for i:=(m div 2) downto 1 do heapify(i);for i:=m downto 2 do begintemp:=ai; ai:=a1; a1:=temp;temp:=bi; bi:=b1; b1:=temp;temp:=ei; ei:=e1; e1:=temp;dec(mh);heapify(1);end;end;procedure spfa(s:longint);var i,k,l,t:longint;beginfillchar(vis,sizeof(vis),fa
17、lse);for i:=1 to n do di:=maxlongint;ds:=0;l:=0;t:=1;viss:=true;q1:=s;repeatl:=(l mod (2*MM) +1;k:=ql;for i:=fk to fk+1-1 doif dk+eidbi thenbegindbi:=dk+ei;if not visbi then begint:=(t mod (2*MM) +1;qt:=bi;visbi:=true;end;end;visk:=false;until l=t;end;Beginreadln(n,m);for i:=1 to m do beginreadln(ai
18、,bi,ei);ai+m:=bi;bi+m:=ai;ei+m:=ei;end;m:=m+m;heap;for i:=1 to m do if fai=0 then fai:=i;fn+1:=m+1;for i:=n downto 1 do if fi=0 then fi:=fi+1;readln(s,t);spfa(s);writeln(dt);end. 邻接矩阵(无向图)以上两种是边集数组储存方式,以下是邻接矩阵(无向图):const MM=100*2;varn,m,i,j:longint;g:array1.MM,1.MM of longint;q,d:array1.MM of longin
19、t;vis:array1.MM of boolean;a,b,e,s,t:longint;procedure spfa(s:longint);var l,t,i,k:longint;beginfillchar(vis,sizeof(vis),false);for i:=1 to n do di:=maxlongint;ds:=0;l:=0;t:=1;q1:=s;viss:=true;repeatl:=l mod MM +1;k:=ql;for i:=1 to n doif (gk,imaxlongint)and(dk+gk,idi) then begindi:=dk+gk,i;if not visi then begint:=t mod MM +1;qt:=i;visi:=true;end;end;visk:=false;until l=t;end;beginreadln(n,m);for i:=1 to n dofor j:=1 to n do gi,j:=maxlongint;for i:=1 to m dobeginreadln(a,b,e);ga,b:=e;gb,a:=e;end;readln(s,t);spfa(s);writeln(dt);end.扩展阅读: 1. 2.
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1