k短路

48 篇文章 0 订阅
7 篇文章 0 订阅
本来很早以前就用线段树写了一遍,但是居然mle了。。。
先用dij求各点到汇点的最短路f[i],再从源点bfs A*搜,g[j]:=g[i]+c[i,j];g表示到源点距离,每次扩展都选g[i]+f[i]最小的点,向其他点扩展一种状态而不是点,所以优先队列中的点可能超过n。

优先队列可以用堆实现,用线段树会爆空间,但是我用惯了zkw线段树,不会写二叉堆,于是用可并堆实现了。

const max=1073741819;maxt=500000;
var v,tail,tail2:array[1..2000]of longint;
    f:array[0..2000]of longint;
    b:array[1..4096]of longint;
    cost,cost2,sora,sora2,next,next2:array[1..102000]of longint;
    nt,st,l,r,g,d:array[0..maxt]of longint;
    m,k,s,t,ss,ss2,tl,m1,n,r1,h:longint;
procedure inf;
begin
 assign(input,'2449.in');reset(input);
 assign(output,'2449.out');rewrite(output)
end;
procedure ouf;
begin
 close(input);close(output)
end;
procedure origin;
var i:longint;
begin
 m1:=1;
 while m1<=n+2 do m1:=m1<<1;
 for i:=1 to n do b[i+m1]:=i;
 for i:=1 to maxt-1 do nt[i]:=i+1;tl:=maxt;
 for i:=1 to n do tail[i]:=i;ss:=n;
 for i:=1 to n do tail2[i]:=i;ss2:=n
end;
function min(x,y:longint ): longint;
begin
 if d[x]<d[y] then exit(x) else exit(y)
end;
procedure change(x,w:longint);
begin
 d[x]:=w;x:=(x+m1)>>1;
 while x<>0 do begin
  b[x]:=min(b[x<<1],b[x<<1+1]);
  x:=x>>1
 end
end;
procedure dij(s:longint);
var i,x,ne,cos:longint;
begin
  fillchar(d,sizeof(d),127);fillchar(f,sizeof(f),127);
  change(s,0);
  while d[b[1]]<max do begin
    x:=b[1];cos:=d[x];f[x]:=cos;change(x,max);
    i:=x;
    while next2[i]<>0 do begin
     i:=next2[i];ne:=sora2[i];
     if d[ne]<>max then
      if cos+cost2[i]<d[ne] then change(ne,cos+cost2[i])
    end
  end
end;
procedure ori(x,w:longint);
begin
 st[r1]:=x;g[r1]:=w;
 l[r1]:=0;r[r1]:=0;d[r1]:=0;
 r1:=nt[r1]
end;
procedure del(x:longint);
begin
 st[x]:=0;g[x]:=0;
 l[x]:=0;r[x]:=0;d[x]:=0;
 nt[tl]:=x;tl:=x;
 nt[tl]:=0
end;
function merge(x,y:longint ): longint;
var e:longint;
begin
  if x=0 then exit(y);
  if y=0 then exit(x);
  if g[x]+f[st[x]]>g[y]+f[st[y]] then begin
    e:=x;x:=y;y:=e
  end;
  r[x]:=merge(r[x],y);
  if d[l[x]]<d[r[x]] then begin
    e:=l[x];l[x]:=r[x];r[x]:=e
  end;
  d[x]:=d[r[x]]+1;
  exit(x)
end;
function bfs(s:longint):longint;
var x,rr,hh,ne,i,cos:longint;
begin
 fillchar(v,sizeof(v),0);
 r1:=1;h:=1;
 ori(s,0);
 repeat
  x:=st[h];cos:=g[h];i:=x;
  inc(v[x]);
  if (x=t)and(v[x]=k) then exit(cos+f[x]);
  hh:=h;
  h:=merge(l[h],r[h]);
  del(hh);
  while next[i]<>0 do begin
    i:=next[i];ne:=sora[i];
    if f[ne]<max then begin
     rr:=r1;
     ori(ne,cos+cost[i]);
     h:=merge(h,rr)
    end
  end
 until h=0;
 exit(-1)
end;
procedure link(x,y,z:longint);
begin
 inc(ss);next[tail[x]]:=ss;tail[x]:=ss;sora[ss]:=y;cost[ss]:=z;
 inc(ss2);next2[tail2[y]]:=ss2;tail2[y]:=ss2;sora2[ss2]:=x;cost2[ss2]:=z
end;
procedure init;
var i,x,y,z:longint;
begin
 readln(n,m);
 origin;
 for i:=1 to m do begin
   readln(x,y,z);
   link(x,y,z)
 end;
 readln(s,t,k);
 if s=t then inc(k);
 dij(t);
 if f[s]>max then begin
  writeln(-1);exit
 end;
 fillchar(d,sizeof(d),0);
 writeln(bfs(s))
end;
begin
  inf;
  init;
  ouf
end.


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值