一个看错题面带来的问题——最小标号最短路

6 篇文章 0 订阅
3 篇文章 0 订阅

有一个问题我看错了题面,不会做,问Anque,却发现看错的题意却有精妙做法。

题意简述:有一幅n个点,m条边的有向图(2<=n<=100000,1<=m<=200000),求1到n最短路中标号最小的路径。

首先,做S点的最短路,如果dis(x)+d(x,y)=dis(y),则边(x,y)可能在S点出发的最短路上。

这题精妙之处在于如果求出了源点的单源最短路不能保证是标号最小的路径,如果挑可能在最短路上的边走,不一定能走到汇点。
精妙做法就是把边反向,做汇点的单元最短路,从源点走可能在汇点最短路上的边,并且在走的时候,时刻选标号最小的点。因为在源点走可能在汇点最短路上的边必然走的是到汇点的最短路,这时候挑标号最小的点是没有问题的;如果做了源点的最短路,不能保证走可能在源点最短路上的边能走到汇点。

const 
  mxn=100001;
  INF=maxlongint>>1;
type
  point=record
          y,v,next:longint;
        end;
var
  map:array[0..500000] of point;
  first,dis,q,a:array[0..mxn] of longint;
  inque:array[0..mxn] of boolean;
  n,m,s,x,y,v,head,tail,t,i:longint;
procedure ins(x,y,v:longint);
  begin
    inc(s);
    map[s].y:=y;
    map[s].v:=v;
    map[s].next:=first[x];
    first[x]:=s;
  end;
begin
  fillchar(first,sizeof(first),0);s:=1;
  readln(n,m);
  for i:=1 to m do
    begin
      read(x,y,v);
      ins(x,y,v);
      ins(y,x,v);
    end;
  for i:=1 to n do dis[i]:=INF;
  fillchar(inque,sizeof(inque),false);
  head:=1;tail:=2;
  q[head]:=n;dis[n]:=0;inque[n]:=true;
  while head<>tail do
    begin
      x:=q[head];
      t:=first[x];
      while t>0 do
        begin
          y:=map[t].y;
          if (t and 1=1)and(dis[x]+map[t].v<dis[y]) then
            begin
              dis[y]:=dis[x]+map[t].v;
              if not inque[y] then
                begin
                  inque[y]:=true;
                  q[tail]:=y;
                  inc(tail);if tail>mxn then tail:=1;
                end;
            end;
          t:=map[t].next;   
        end;
      inque[x]:=false;
      inc(head);if head>mxn then head:=1;
    end;
  s:=1;
  a[s]:=1;
  while a[s]<>n do
    begin
      x:=a[s];
      t:=first[x];
      inc(s);a[s]:=INF;
      while t>0 do
        begin
          if (t and 1=0)and(dis[x]-map[t].v=dis[map[t].y])and(map[y].y<a[s])
            then a[s]:=map[t].y;
          t:=map[t].next;
        end;
    end;
  writeln(dis[1]);
  for i:=1 to s-1 do
    write(a[i],' ');
  writeln(a[s]);    
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值