图(一)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)!

(不知道堆的点击:http://zhidao.baidu.com/linkurl=2yAgZ6jGP891MWc_ZkdBA5OVp2aXLRNsz_lwlf_6cNkbeY-dYvc1iljnnVTgcyi8FdmgV_wtp9XBWj54oLvdFK

 程序如下,很长,用到邻接表,见图(一),链接在上面,实用性会到后面谈及  

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的也会出现超时的现象,一定要看数据范围再做选择

posted on 2015-08-26 20:53  zzm1  阅读(393)  评论(1编辑  收藏  举报