图(一)http://www.cnblogs.com/ZZMbk/p/4759952.html
已经讲了储存,接下来看最短路:
从点1到点5最短路径就是最短路,
①.Floyd
主要用于每对点的最短路,效率是O(n3),可以想象速度有多慢!
核心:d[i,j]:=min(d[i,j],d[i,k]+d[k,j]);
初始化条件:d[i,i]:=0 //自己到自己为0;对角线为0;
d[i,j]=边权,i与j有直接相连的边
d[i,j]= +∞ ,i与j无直接相连的边。 // 一般设为: maxint/2 or maxlongint/2;
//以下常用于一个点到所有点间的最短路
②.Bellman-Ford
核心:d[i]=min{d[j]+边权| j满足i与j有直接相连的边}
d[start(起始点)]=0,d[i(i<>start)]=+∞
程序不给,因为SPFA可以取代它,这两个的优势是可判断负权回路(自己查),
时效为O(n*m) n--点数,m--边数
③.Dijstra
缺点:无法判断负权回路
思想:1.找到最短距离已经确定的顶点,从它出发更新相邻顶点的最短距离;
2.此后将1中的最短距离已经确定的顶点移除,再新找一个,回到1,直到所有点全部找到;
刚开始最短距离已经确定的顶点是d[start(begin)]=0,
可以得出时间效率为O(n2),
但是用堆来保存更新的最短距离以及顶点,再用其取出,则时间效率降低为O(m*log n)!
程序如下,很长,用到邻接表,见图(一),链接在上面,实用性会到后面谈及
type
point=^node;
node=record val,q:integer; next:point; end; type arr=record bh,zhi:longint; end;//bh是储存点的编号,zhi是储存从点start到点bh更新的最短距离(更新次数可能有好几次) var a:array[1..100000]of point; head,p:point;//邻接表 f:array[1..100000]of longint;//记录从点start到点i的最短距离 dui:array[1..100000]of arr;//堆,用处在上面提到 used:array[1..100000]of boolean;//记录点i是否用过 n,m,x,y,be,en,i,j,minw,v,dans:longint; procedure swap(var a,b:arr); var t:arr; begin t:=a;a:=b;b:=t; end;//交换 procedure up(k:longint); var p:longint; begin p:=k; if p=1 then exit; while dui[p].zhi<dui[p div 2].zhi do begin swap(dui[p],dui[p div 2]); p:=p div 2; if p=1 then break; end; end; procedure down; var p,min,minw:longint; begin p:=1; if p*2>dans then exit; if p*2=dans then begin min:=dui[p*2].zhi;minw:=p*2; end else if dui[p*2].zhi<dui[p*2+1].zhi then begin min:=dui[p*2].zhi;minw:=p*2; end else begin min:=dui[p*2+1].zhi;minw:=p*2+1; end; while dui[p].zhi>min do begin swap(dui[p],dui[minw]); p:=minw; if p*2>dans then break; if p*2=dans then begin min:=dui[p*2].zhi;minw:=p*2; end else if dui[p*2].zhi<dui[p*2+1].zhi then begin min:=dui[p*2].zhi;minw:=p*2; end else begin min:=dui[p*2+1].zhi;minw:=p*2+1; end; end; end;
//up和down是堆的操作,可以用来排序,速度和快排一样,我的down操作有点麻烦 begin assign(input,'dij.in'); assign(output,'dij.out'); reset(input);rewrite(output); readln(n,m,be,en);//n是点数,m是边数,be是起点,en是终点 for i:=1 to n do begin a[i]:=nil;f[i]:=maxlongint;used[i]:=false; end;//一系列初始化 for i:=1 to m do begin readln(x,y,j); new(p);p^.q:=y;p^.val:=j;p^.next:=a[x];a[x]:=p; new(p);p^.q:=x;p^.val:=j;p^.next:=a[y];a[y]:=p;//建立邻接表 end; f[be]:=0; dui[1].zhi:=0;dui[1].bh:=be;dans:=1;
//堆的初始化 while dans<>0 do begin minw:=dui[1].bh;if minw=0 then break; head:=a[minw];used[minw]:=true;
//取出堆中第一个点及值 while head<>nil do begin if (dui[1].zhi+head^.val<f[head^.q])and(not used[head^.q]) then begin f[head^.q]:=dui[1].zhi+head^.val;//更新f数组 inc(dans);dui[dans].zhi:=f[head^.q];dui[dans].bh:=head^.q; up(dans);//往堆中加点及值,并维护点 end; head:=head^.next; end;//更新 while used[dui[1].bh] do begin dui[1].zhi:=dui[dans].zhi;dui[1].bh:=dui[dans].bh; dec(dans);if dans<=0 then break;down; end;
//若堆中第一个点已经用过,则将这个元素移走,再维护堆,直到第一个点没用过 end; writeln(f[en]);
close(input);close(output); end.
程序看起来长,理解了,也不难打。
④.SPFA
type point=^node node=record val,q:longint; next:point; end; var a:array[1..10000]of point; headp,p:point; q:array[1..10000] of longint; used:array[1..10000] of boolean; f:array[1..10000] of longint; n,m,i,j,x,y,head,tail,be,en:longint; begin fillchar(q,sizeof(q),0); head:=0; tail:=0; //队列清空,head是头指针,tail是尾指针 fillchar(used,sizeof(used),false); //used[i]判断i是否在队列中 readln(n,m,be,en); for i:=1 to n do begin f[i]:=maxlongint; //初始化最小值 a[i]:=nil; end; for i:=1 to m do begin readln(x,y,j); new(p);p^.val:=j;p^.q:=y;p^.next:=a[x];a[x]:=p; new(p);p^.val:=j;p^.q:=x;p^.next:=a[y];a[y]:=p; end; inc(t);q[t]:=be;used[be]:=true;f[be]:=0; while head<>tail do //当头尾指针相等时,已做完 begin head:=(head mod n)+1;//mod n是限制队列长度,因为只有n个点 x:=q[head]; used[x]:=false; headp:=a[x]; while headp<>nil do begin if f[x]+headp^.val<f[headp^.q]) then begin //松弛 f[headp^.q]:=f[x]+headp^.val; if not(used[i]) then //如果未入队,则进队 begin t:=(t mod n)+1; q[t]:=i; used[i]:=true;//进队 end; end; headp:=headp^.next; end; end; writeln(f[en]); end.
时间效率是O(kE),k的平均值是2
③④是比赛中常用的,当边特别多时,④的速度并不快,所以遇到稠密图时需要用到③(优化后的),
比赛时,有些题目往往出稠密图,选SPFA的也会出现超时的现象,一定要看数据范围再做选择