zoukankan      html  css  js  c++  java
  • 最短路5解

    题目模板:最短路

    https://www.luogu.org/problem/show?pid=3371

    千年不过的不用指针的spfa;
    原来各位dalao是骗我的!
    根本不用ff数组来判重!
    加上这题目pas题解比较少就来谈谈5种AC的思路(顺便总结一下);
    ①floyd算法 时间:O(n^3);空间:O(n^2);
    对于20%的数据:N<=5,M<=15
    对于40%的数据:N<=100,M<=10000

    ②dijkstra算法
    时间: O(n^2); 空间:O(n^2);
    对于70%的数据:N<=1000,M<=100000

    uses math; 
    const maxn=5000;
    var g:array[1..maxn,1..maxn] of longint;
        d:array[1..maxn] of longint; //pre[i]指最短路径上I的前驱结点
        n,m,s,x,y,w,i,j:longint;
    procedure dijkstra(v0:longint);
     var u:array[1..maxn] of boolean;
         i,j,k:longint; min:longint;
    begin
      fillchar(u,sizeof(u),false);
      for i:=1 to n do d[i]:=g[v0,i];
      d[v0]:=0;
      u[v0]:=true;
      for i:=1 to n-1 do begin   //每循环一次加入一个离1集合最近的结点并调整其他结点的参数
        min:=2147483647; k:=0; //k记录离1集合最近的结点
        for j:=1 to n do
          if (not u[j]) and (d[j]<min) then begin
            k:=j; min:=d[j];
        end;
        if k=0 then break;
        u[k]:=true;
        for j:=1 to n do
          if (not u[j]) and (g[k,j]+d[k]<d[j]) then d[j]:=g[k,j]+d[k];
      end;
    end;
    begin
      readln(n,m,s);
      for i:=1 to n do
        for j:=1 to n do if i=j then g[i,j]:=0 else g[i,j]:=2147483647;
      for i:=1 to m do begin
        readln(x,y,w);
        g[x,y]:=min(w,g[x,y]);
      end;
      dijkstra(s);      //找出最短路径
      for i:=1 to n do write(d[i],' ');
      writeln;
    end.

    ③dijkstra+前向星空间优化+堆优化
    时间: O(n log n); 空间:O(kn);
    对于100%的数据:N<=10000,M<=500000

    type rec=record
     pre,en,w:longint;
    end;
    rec2=record
     id,val:longint;
    end;
    const inf=233333333;
          maxm=500000;
          maxn=10000;
    var i,j,n,m,s,x,y,z,tot,nd:longint;
        d,head:array[-maxn..maxn]of longint;
        a:array[-maxm..maxm]of rec;
        dui:array[0..4*maxm]of rec2;
    procedure swap(var a,b:rec2);
    var t:rec2;
    begin
     t:=a; a:=b; b:=t;
    end;
    procedure adde(u,v,w:longint);
    begin
     inc(tot); a[tot].en:=v;
     a[tot].pre:=head[u];
     head[u]:=tot;
     a[tot].w:=w;
    end;
    procedure swap(var a,b:longint);
    var t:longint;
    begin t:=a;a:=b;b:=t;end;
    procedure up(x:longint);//将一个结点“上浮”
    begin
      while x>1 do begin //没有上浮到最顶层
        if dui[x].val>dui[x div 2].val then break;//如果上方的结点小于此节点,则暂停上浮
        swap(dui[x],dui[x div 2]);//交换上方结点与此结点
        x:=x div 2;
      end;
    end;
    procedure down(x:longint);//将一个节点“下沉”
      var y:longint;
    begin
      while x<nd do begin
        y:=x+x;//y是x的左儿子
        if y>nd then break;//x已经沉到底部
        if (y<nd)and(dui[y+1].val<dui[y].val) then inc(y);//如果x存在右儿子,且右儿子比左儿子小,则将y赋值到右儿子
        if dui[x].val<=dui[y].val then break;//若两个儿子中的较小值仍然比x大,则停止下沉
        swap(dui[x],dui[y]);//下沉
        x:=y;
      end;
    end;
    function pop():longint;
    begin
      pop:=dui[1].id;
      swap(dui[1],dui[nd]);//将最后的结点(保证其没有儿子)与最顶端交换
      dec(nd);
      down(1);//下沉顶端
    end;
    procedure dijkstra(v0:longint);
    var i,j,k,minn,u,v,p:longint;
        vis:array[-maxn..maxn]of boolean;
    begin
     fillchar(vis,sizeof(vis),false);
     for i:=1 to n do d[i]:=inf;
     d[v0]:=0;
     dui[1].val:=0;
     dui[1].id:=v0;
     nd:=1;
     for i:=1 to n do begin
      u:=pop();
      while vis[u] and (nd>0) do u:=pop();
      vis[u]:=true;
      p:=head[u];
      while p>0 do begin
       v:=a[p].en;
       if (not vis[v]) and(d[u]+a[p].w<d[v]) then begin
        d[v]:=d[u]+a[p].w;
        inc(nd);
        dui[nd].id:=v;
        dui[nd].val:=d[v];
        up(nd);
       end;
       p:=a[p].pre;
      end;
     end;
    end;
    begin
     readln(n,m,s);
     for i:=1 to m do begin
      readln(x,y,z);
      adde(x,y,z);
     end;
     dijkstra(s);
     for i:=1 to n do
     if d[i]=inf then write(2147483647,' ')else write(d[i],' ');
     writeln;

    ④ spfa队列优化+指针邻接表空间优化
    时间: O(KE); 空间:O(KE);
    对于100%的数据:N<=10000,M<=500000;

    type pp=^node;
          node=record
          en,w:longint;
          pre:pp;
          end;
    const maxn=50040;
    var dis:array[0..maxn]of longint;
        a:array[0..maxn]of pp;
        n,m,b,u,v,x,y,i,j,l,w,s:longint;
    procedure adde(u,v,w:longint);
    var p:pp;
    begin
     new(p);
     p^.pre:=a[u]^.pre;
     p^.en:=v; p^.w:=w;
     a[u]^.pre:=p;
    end;
    procedure spfa;
    var f:array[0..maxn]of boolean;
        q:array[0..4*maxn]of longint;
        p:pp;
        head,tail,u,v:longint;
    begin
     q[1]:=s; dis[s]:=0;
     head:=0; tail:=1;
     fillchar(f,sizeof(f),false);
     while head<tail do begin
      inc(head); u:=q[head]; p:=a[u];
      while p^.pre<>nil do begin
       p:=p^.pre; v:=p^.en; w:=p^.w;
       if dis[v]>dis[u]+w then begin
        dis[v]:=dis[u]+w;
        if not f[v] then begin
         f[v]:=true;
         inc(tail); q[tail]:=v;
        end;
       end;
      end;
      f[u]:=false;
     end;
    end;
    begin
     readln(n,m,s);
     for i:=1 to n do begin
      new(a[i]);
      a[i]^.pre:=nil;
     end;
     for i:=1 to m do begin
      readln(u,v,w);
      adde(u,v,w);
     end;
      for i:=1 to n do
      dis[i]:=maxlongint div 3;
     spfa;
    // writeln;
     for i:=1 to n do
      if dis[i]=maxlongint div 3 then write('2147483647 ')
      else write(dis[i],' ');
     close(input);
     close(output);
    end.

    ⑤ spfa队列优化+链式前向星空间优化
    时间: O(KE); 空间:O(KE);
    对于100%的数据:N<=10000,M<=500000

    type rec=record
     w,en,pre:longint;
    end;
    var n,m,s,x,y,z,tot,i:longint;
        head,q,d:array[1..1000000]of longint;
        a:array[1..1000000]of rec;
    procedure adde(u,v,w:longint);
    begin
     inc(tot);
     a[tot].en:=v;
     a[tot].pre:=head[u];
     a[tot].w:=w;
     head[u]:=tot;
    end;
    procedure spfa;
    var h,t,i,v,u:longint;
    begin
     for i:=1 to n do d[i]:=maxlongint;
     q[1]:=s; d[s]:=0;
     h:=0; t:=1;
     while h<t do begin
      inc(h);
      u:=q[h];
      i:=head[u];
      while i>0 do begin
       v:=a[i].en;
       if d[v]=maxlongint then begin
        d[v]:=d[u]+a[i].w;
        inc(t);q[t]:=v;
       end else
       if d[v]-d[u]>a[i].w then begin
        inc(t);q[t]:=v;
        d[v]:=a[i].w+d[u];
       end;
       i:=a[i].pre;
      end;
     end;
    end;
    begin
     readln(n,m,s);
     for i:=1 to m do begin
      readln(x,y,z);
      adde(x,y,z);
     end;
     spfa;
     for i:=1 to n do
      if d[i]=maxlongint then write('2147483647 ')
      else write(d[i],' ');
    end.
  • 相关阅读:
    最长上升子序列问题总结
    Problem C
    Problem C
    Problem P
    Problem P
    Problem H
    Problem H
    Problem D
    Problem D
    Linux系统调用--getrlimit()与setrlimit()函数详解
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/7622200.html
Copyright © 2011-2022 走看看