[转]浅谈dijkstra堆优化

众所周知的,dijkstra是图论算法中求单源最短路的一种简单求法。可能有人会说SPFA比dijkstra要实用,而且可以用于求存在负边权的情况,但是dijkstra有着他的优点——其运行速度上优于SPFA。 (PS.需要堆进行优化。)

我们先看一道经典(水)题:

平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。

若有连线,则表示可从一个点到达另一个点,即两点之间有通路,通路的距离为两点之间的直线距离。现在的任务是找出从入点到出点之间的最短路程。

题目很简单,是最短路径的改版。由于dijkstra的算法简单来讲就是搜索和贪心的组合,所以代码可以很轻松得出:

Var

a:array[1..100,1..2]of longint;

visit:array[1..100]of boolean;

c:array[1..100]of real;

f:array[1..100,1..100]of real;

n,i,j,k,x,y,m,s,e:longint;

min:real;

Begin

readln(n);//读入点数n

for i:=1 to n do

read(a[i,1],a[i,2]);//a[i,1],a[i,2]分别记录第i点的横坐标和纵坐标

readln(m);//读入边数m

fillchar(f,sizeof(f),$ 5f);//f用于记录

for i:=1 to m do

begin

readln(x,y);

f[x,y]:=sqrt(sqr(a[x,1]-a[y,1])+sqr(a[x,2]-a[y,2]));//使用勾股定理求出两点之间的距离

f[y,x]:=f[x,y];//无向图,双向记边

end;

readln(s,e);//s,e为入点和出点

for i:=1 to n do

c[i]:=f[s,i];//为入点进行预处理

for i:=1 to n-1 do

begin

min:=maxlongint;

k:=0;

for j:=1 to n do

if (not visit[j]) and (c[j]

then begin

min:=c[j];

k:=j;

end;//寻找一个离当前点最近且未被访问过的点

if k=0

then break;

visit[k]:=true;//记录改点已经到达过

for j:=1 to n do

if c[k]+f[k,j]

then c[j]:=c[k]+f[k,j];//记录最小值到f中

end;

writeln(c[e]:0:2);//输出到出点的最短路径

End.

当然,我们知道,这是非堆优化的dijkstra,其复杂度打到了O(n²),其与SPFA相比毫无优势可言。因此我们要对其进行堆优化,使其的复杂度降到O(n*log(n))。说白了,就是将贪心改成堆,从而使复杂度降到O(log(n))当然,为了进一步优化,我们可以用映射表来开G加速。一经典的最远路径为例题,代码如下:

Var
  n,m,num,a,b,c,i,j:longint;
  ed,from,val,son,next,intoout,outtoin,dis,d:array[1..2000001]of longint;
  visit:array[1..2000001]of 0..1;
Procedure swap(var a,b:longint);
  var
    temp:longint;
  begin
    temp:=a;
    a:=b;
    b:=temp;
  end;
Procedure down(l:longint);
  begin
    while ((l<n)and (d[l]>d[l shl 1+1]))or ((l<n)and (d[l]>d[l shl 1])) do
      if d[l shl 1+1]>d[l shl 1]
        then begin
               swap(d[l shl 1+1],d[l]);
               swap(intoout[l],intoout[l shl 1+1]);
               swap(outtoin[intoout[l]],outtoin[intoout[l shl 1+1]]);
               l:=l shl 1+1;
             end
        else begin
               swap(d[l shl 1],d[l]);
               swap(intoout[l],intoout[l shl 1]);
               swap(outtoin[intoout[l]],outtoin[intoout[l shl 1]]);
               l:=l shl 1;
             end;
  end;//与普通堆操作相同,只是还要留心对映射表的操作
Procedure up(l:longint);
  begin
    while(l>1) and(d[l shr 1]>d[l]) do
      begin
        swap(d[l shr 1],d[l]);
        swap(intoout[l],intoout[l shr 1]);
        swap(outtoin[intoout[l]],outtoin[intoout[l shr 1]]);
        l:=l shr 1;
      end;
  end;//与普通堆操作相同,只是还要留心对映射表的操作
Procedure dijkstra(x:longint);
  var
    i,len,edd:longint;

begin
    fillchar(d,sizeof(d),$7f);
    fillchar(dis,sizeof(dis),$7f);
    fillchar(visit,sizeof(visit),0);//数组的初始化,不解释
    for i:=1 to n do
      begin
        outtoin[i]:=i;//表示堆外映射堆内
        intoout[i]:=i;//表示堆内映射堆外
       end;//为了加快处理速度,我们可以开一张映射表以代替路径数组的维护
    d[x]:=0;//对入点首先进行拓展
    len:=n;//由于后面需要对点书进行操作与修改,此处用len记录n以方便操作
    up(x);//对现在的堆进行维护PS.本步不可少,即使是本程序中,我们也可以看到,0不一定在第一个位置。
    while (visit[n]=0)and(len>0) do//ps.由于可以理解为求1到n的最短路,因此当第n点被访问时,程序已经结束。当题目大意与本题不符合时,(visit[n]=0)应舍去。
      begin
        edd:=intoout[1];//以当前点为起点,记录拓展花费的最小代价的点
        dis[edd]:=d[1];//将该点记录到最小路径的数组中
        visit[intoout[1]]:=1;//表示该点已经被访问
        j:=son[edd];
        d[1]:=d[len];
        intoout[1]:=intoout[len];
        outtoin[intoout[1]]:=1;
        dec(len);//以上都为删堆的操作
        down(1);//一轮操作完了,接着对堆进行维护
        while j<>0 do
          begin
            if (visit[ed[j]]=0)and(val[j]+dis[edd]<d[outtoin[ed[j]]])
              then begin
                     d[outtoin[ed[j]]]:=val[j]+dis[edd];
                     up(outtoin[ed[j]]);
                   end;//拓展(更新)到达堆中点的最短路径
            j:=next[j];//对下一条边进行拓展
          end;
      end;
     end;
Begin
  readln(n,m);//读入点数与边数
  num:=0;//num用于记录边的条数
  for i:= 1 to m do
    begin
      read(a,b,c);//读入a,b,表示两点之间有通路,且路径长度为c
      inc(num);//每读入一条边,边数自然要加一
      from[num]:=a;//from数组用于记录第num条边的出点,相当于是树中的父节点。PS.在经典的最短路径求法中,这步可以不写
      ed[num]:=b;//ed数组用于记录第num条边的终点
      val[num]:=c;//val用于记录第num条边的路径长度
      next[num]:=son[a];
      son[a]:=num;//链表记边时,插入的元素要放在链表(son)首部,同时,将原来的首部放在新元素的后面(next)
      inc(num);//同样的,本题中需要双向记边
      from[num]:=b;
      ed[num]:=a;
      val[num]:=c;
      next[num]:=son[b];
      son[b]:=num;
    end;//那么到这里,读入就完成了。接下来就是dijkstra的主要部分了
  dijkstra(1);//最短路径问题,其实也可以转化为1——n之间的最短路。
  write(dis[n]);//本题也可以转化为1——n之间的最短路,因此只要输出n的最短路即可
End.

end;//那么到这里,读入就完成了。接下来就是dijkstra的主要部分了

dijkstra(1);//最短路径问题,其实也可以转化为1——n之间的最短路。

write(dis[n]);//本题也可以转化为1——n之间的最短路,因此只要输出n的最短路即可

End.

时间: 2024-10-20 16:19:05

[转]浅谈dijkstra堆优化的相关文章

hdu 2544 单源最短路问题 dijkstra+堆优化模板

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 41168    Accepted Submission(s): 17992 Problem Description 在每年的校赛里.全部进入决赛的同学都会获得一件非常美丽的t-shirt.可是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的.所以如今他们想要寻

Bzoj 2834: 回家的路 dijkstra,堆优化,分层图,最短路

2834: 回家的路 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 62  Solved: 38[Submit][Status][Discuss] Description Input Output Sample Input 2 1 1 2 1 1 2 2 Sample Output 5 HINT N<=20000,M<=100000 Source dijkstra+堆优化+分层图 把所有的横向和纵向分开看.跑最短路即可. 注意:N这么大,不能写

浅谈尾递归的优化方式

尾递归与Continuation 浅谈尾递归的优化方式

POJ 3013 Big Christmas Tree【最短路变形,DIjkstra堆优化+spfa算法】

Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 23064   Accepted: 4987 Description Christmas is coming to KCM city. Suby the loyal civilian in KCM city is preparing a big neat Christmas tree. The simple structure of t

【日常学习】【Dijkstra堆优化】codevs2038 香甜的黄油题解

转载请注明出处 [ametake版权所有]http://blog.csdn.net/ametake 先放上题目,出自USACO 题目描述 Description 农夫John发现做出全威斯康辛州最甜的黄油的方法:糖.把糖放在一片牧场上,他知道N(1<=N<=500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油.当然,他将付出额外的费用在奶牛上. 农夫John很狡猾.他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场.他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶. 农

Heap+Dijkstra堆优化的Dijkstra

前面说到"原生的Dijkstra",由于Dijkstra采用的是贪心策略,在贪心寻找当前距离源结点最短的结点时需要遍历所有的结点,这必然会导致效率的下降,时间复杂度为n^n.因此当数据量较大时会消耗较长时间.为了提高Dijkstra的效率,只有对Dijkstra的贪心策略进行改进. 由于Dijkstra采用的贪心策略是每次寻找最短距离的结点并将其放入存放所有已知最短距离结点的S集合中,可以联想到堆以及优先级队列这些数据结构,这些结构都能非常高效地提供当前状态距离最短的结点.实践也可以证

Poj 1151 Invitation Cards dijkstra 堆优化

很裸的最短路,不过节点数和边数都是1e6,直接dij肯定是不行了,稀疏图用heap优化一下就好 o(╯□╰)o注意STL里面的优先队列是优先级大的(值大的)在前面的,一开始没注意WA了好几发,哎,太粗心了 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #inc

浅谈前端性能优化(移动端)

上一篇讲了PC端的部分:前端性能优化(PC端),这次继续说移动端的.相对于PC端的,移动web浏览器上有一些明显的特点:设备的屏幕小.新特性兼容性较好.支持一些比较新的HTML5和CSS3.需要与Native应用交互等.但移动端可用的CPU资源和网络资源极为有限,因此要做好移动端web上的优化往往需要考虑做更多的事情.首先在移动web的前端页面渲染中,PC的优化规则同样适用,此外针对浏览器也要做一些更细节的优化达到更好的效果.需要注意的是,并不是移动端的优化在PC端不适用,而是由于兼容性的原因,

lfyzoj1096 最短路径问题 dijkstra堆优化

题目描述 平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间.其中的一些点之间有连线. 若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离.现在的任务是找出从一点到另一点之间的最短路径. 输入 第一行为整数n. 第2行到第n+1行(共n行) ,每行两个整数x和y,描述了一个点的坐标. 第n+2行为一个整数m,表示图中连线的个数. 此后的m 行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线. 最后一行:两个整数