bzoj 1598 K短路 A*

61 篇文章 0 订阅
9 篇文章 0 订阅

题意:n个点,起点为n终点为1,m条有向边。求前K短路,如果不存在输出-1

涨姿势时刻——A*算法

A*算法的核心在于它的估值函数的设计上:  f(n)=g(n)+h(n) 
其中,

f(n)是每个可能试探点的估值;

g(n)表示从S到n的实际代价

h(n)表示从n到T的估值(h(n)设计的好坏,直接影响着具有此种算法的是否能称为A*算法)。

具有f(n)=g(n)+h(n)策略的启发式算法能成为A算法的充分条件是:

①存在着从S到T的最优路径。(保证有路径
②问题域是有限的。
③所有结点的子结点的搜索代价值>0。(无负边
④h(n) <= h * (n) (h*(n)为实际问题的代价值)。

一般的搜索前三条都可以满足,而第四点就要视情况而定了。 

=========================_(:з」∠)_==================================

此题为K短路模板题

也是A*解决的经典题

K短路的定义:假设从S出发,有M条长度不同的路径可以到达点T,则K短路就是这M条路径中第K小的路径长度。

此题中,f(n)表示我们的估价函数,g(n)表示当前从S到n走的实际距离,h(n)为n走到T的最优距离

即    估价函数=当前值+当前位置到终点的最优距离

具体实现:

(1)将有向图的所有边反向,在反图上,以原终点为S’,原起点为T',求S'到所有点的最短距离(在反图上预处理出h(x)); 

(2)新建一个优先队列(小根堆),加入原起点S;(如果不存在从S到T的最短路直接退出) 

(3)每次取出f(x)最小的点x,统计x的取出次数

①如果点x就是原终点T, 那么如果当前为T的第k次出队,则当前路径的长度(即f(t),因为h(t)=0,此时的f值也就是g值)就是当前从S到T的第k短路的长度,

②否则遍历原图与p相连的所有的边,将扩展出邻接点p的信息加入优先级队列(小根堆)  f(p)=g(p)+h(p)=g(x)+len+dis[p]=f(x)-h(x)+len+dis[p]=f(x)-dis[x]+len+dis[p];

玄学复杂度

type
        rec=record
            x,f:longint;
end;

var
        n,m,k,x,y,z,l,l2:longint;
        size            :longint;
        i               :longint;
        pre,other,len   :array[0..10010] of longint;
        pre2,other2,len2:array[0..10010] of longint;
        heap,pos,times  :array[0..1010] of longint;
        dis             :array[0..1010] of int64;
        ans             :array[0..110] of longint;
        last,last2      :array[0..1010] of longint;
        heap2           :array[0..5000010] of rec;

procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

procedure swap2(var a,b:rec);
var
        c:rec;
begin
   c:=a; a:=b; b:=c;
end;

procedure connect(x,y,z:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   len[l]:=z;
end;

procedure connect2(x,y,z:longint);
begin
   inc(l2);
   pre2[l2]:=last2[x];
   last2[x]:=l2;
   other2[l2]:=y;
   len2[l2]:=z;
end;

procedure heap_up(i:longint);
begin
   if i=1 then exit;
   while i>1 do
   begin
      if dis[heap[i]]<dis[heap[i>>1]] then
      begin
         swap(heap[i],heap[i>>1]);
         pos[heap[i]]:=i;
         pos[heap[i>>1]]:=i>>1;
         i:=i>>1;
      end else exit;
   end;
end;

procedure heap_down(i:longint);
var
        t:longint;
begin
   while 2*i<=size do
   begin
      if dis[heap[i]]<dis[heap[i<<1]] then t:=i else t:=i<<1;
      if 2*i+1<=size then
        if dis[heap[t]]>dis[heap[(i<<1)+1]] then t:=(i<<1)+1;
      if i<>t then
      begin
         swap(heap[i],heap[t]);
         pos[heap[i]]:=i;
         pos[heap[t]]:=t;
         i:=t;
      end else exit;
   end;
end;

procedure heap_up2(i:longint);
begin
   if i=1 then exit;
   while i>1 do
   begin
      if heap2[i].f<heap2[i>>1].f then
      begin
         swap2(heap2[i],heap2[i>>1]);
         i:=i>>1;
      end else exit;
   end;
end;

procedure heap_down2(i:longint);
var
        t:longint;
begin
   while i<<1<=size do
   begin
      if heap2[i].f<heap2[i<<1].f then t:=i else t:=i<<1;
      if (i<<1)+1<=size then
        if heap2[t].f>heap2[(i<<1)+1].f then t:=(i<<1)+1;
      if i<>t then
      begin
         swap2(heap2[i],heap2[t]);
         i:=t;
      end else exit;
   end;
end;

procedure dijkstra;
var
        cur,p,q,i:longint;
begin
   for i:=1 to n do dis[i]:=maxlongint*100;
   dis[1]:=0;
   for i:=1 to n do
   begin
      cur:=heap[1];
      heap[1]:=heap[size];
      dec(size);
      heap_down(1);
      //
      q:=last2[cur];
      while (q<>0) do
      begin
         p:=other2[q];
         if dis[p]>dis[cur]+len2[q] then
         begin
            dis[p]:=dis[cur]+len2[q];
            heap_up(pos[p]);
         end;
         q:=pre2[q];
      end;
   end;
end;

procedure work;
var
        curx,curf,p,q:longint;
begin
   if dis[n]=dis[0] then exit;
   heap2[1].x:=n; heap2[1].f:=dis[n]; size:=1;
   while size<>0 do
   begin
      curx:=heap2[1].x; curf:=heap2[1].f;
      heap2[1]:=heap2[size];
      dec(size);
      heap_down2(1);
      //
      inc(times[curx]);
      if curx=1 then ans[times[curx]]:=curf;
      if times[curx]<=k then
      begin
          q:=last[curx];
          while q<>0 do
          begin
             p:=other[q];
             inc(size);
             heap2[size].x:=p;
             heap2[size].f:=curf-dis[curx]+len[q]+dis[p];
             heap_up2(size);
             q:=pre[q];
          end;
      end;
   end;
end;

begin
   read(n,m,k);
   for i:=1 to m do
   begin
      read(x,y,z);
      connect(x,y,z);
      connect2(y,x,z);
   end;
   for i:=1 to n do heap[i]:=i;
   for i:=1 to n do pos[i]:=i;
   size:=n;
   dijkstra;
   work;
   for i:=1 to k do
     if ans[i]=0 then writeln(-1) else writeln(ans[i]);
end.
——by Eirlys




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值