再看最短路算法 1 —— 单源最短路

学了多年的算法,最短路问题相当之常见————

好久没写过最短路的问题了,直到昨天闲的无聊来了一题——BZOJ3402(HansBug:额才发现我弱到只能刷水的地步了TT)

一看这不是明显的单源最短路么呵呵。。。于是直接上来来了个dijkstra,而且用的是邻接表存储图——

Submit之后,结果却是——

技术分享

我立刻被雷到了QAQ。。。于是立刻改写spfa,结果——

技术分享

4000ms+(估计还不止)和192ms究竟是怎样的差距啊QAQ,本人虽然早都听说过spfa的强大性,但是未曾想过差距会如此可怕,于是HansBug‘s Labo Online——

准备:1.dijkstra单源最短路径模板

 1 type
 2     point=^node;
 3     node=record
 4                g,w:longint;
 5                next:point;
 6     end;
 7 
 8 var
 9    i,j,k,l,m,n:longint;
10    a:array[0..100000] of point;
11    b,c,lef,rig,fix:array[0..100000] of longint;
12    p:point;
13 procedure add(x,y,z:longint);inline;
14           var p:point;
15           begin
16                new(p);p^.g:=y;p^.w:=z;
17                p^.next:=a[x];a[x]:=p;
18           end;
19 function min(x,y:longint):longint;inline;
20          begin
21               if x<y then min:=x else min:=y;
22          end;
23 procedure swap(var x,y:longint);inline;
24         var z:longint;
25         begin
26                 z:=x;x:=y;y:=z;
27         end;
28 procedure merge(var x,y:longint);inline;
29         begin
30                 if x=0 then swap(x,y);
31                 if y=0 then exit;
32                 if b[x]>b[y] then swap(x,y);
33                 merge(rig[x],y);
34                 fix[x]:=min(fix[lef[x]],fix[rig[x]])+1;
35                 if fix[lef[x]]<fix[rig[x]] then swap(lef[x],rig[x]);
36         end;
37 function cuthead(var x:longint):longint;
38         begin
39                 c[x]:=1;
40                 cuthead:=b[x];
41                 merge(lef[x],rig[x]);
42                 x:=lef[x];
43         end;
44 procedure dijkstra(x:longint);inline;
45           var i,j,k,l:longint;p:point;
46           begin
47                fillchar(c,sizeof(c),0);
48                fillchar(b,sizeof(b),0);
49                c[x]:=1;
50                p:=a[x];
51                head:=0;l:=0;
52                while p<>nil do
53                      begin
54                           if b[p^.g]=0 then b[p^.g]:=p^.w else b[p^.g]:=min(b[p^.g],p^.w);
55                           p:=p^.next;
56                      end;
57                for i:=1 to n do
58                         if (c[i]=0) and (b[i]>0) then
59                                 begin
60                                         j:=i;
61                                         merge(head,j);
62                                 end;
63                for i:=1 to n-1 do
64                    begin
65                         if head=0 then break;
66                         p:=a[head];l:=cuthead(head);
67                         while p<>nil do
68                               begin
69                                    if (c[p^.g]=0) and ((b[p^.g]=0) or (b[p^.g]>(p^.w+l))) then b[p^.g]:=p^.w+l;
70                                    p:=p^.next;
71                               end;
72                    end;
73           end;
74 begin
75      readln(n,m);
76      for i:=1 to n do a[i]:=nil;
77      for i:=1 to m do
78          begin
79               readln(j,k,l);
80               add(j,k,l);
81          end;
82      dijkstra(1);
83      for i:=1 to n do
84          case c[i] of
85               1:writeln(1, ---> ,i, : ,b[i]);
86               0:writeln(1, ---> ,i, : ,Unavailable);
87          end;
88      readln;
89 end.

2.spfa单源最短路径模板

 1 type
 2         point=^node;
 3         node=record
 4                 g,w:longint;
 5                 next:point;
 6         end;
 7 var
 8         i,j,k,l,m,n:longint;
 9         a:array[0..100000] of point;
10         b,c:array[0..1000000] of longint;
11 procedure add(x,y,z:longint);inline;
12         var p:point;
13         begin
14                 new(p);p^.g:=y;p^.w:=z;
15                 p^.next:=a[x];a[x]:=p;
16         end;
17 procedure spfa(x:longint);inline;
18         var f,r:longint;p:point;
19         begin
20                 f:=1;r:=2;
21                 fillchar(c,sizeof(c),0);
22                 c[x]:=1;
23                 b[1]:=x;
24                 while f<r do
25                         begin
26                                 p:=a[b[f]];
27                                 while p<>nil do
28                                         begin
29                                                 if (c[p^.g]=0) or (c[p^.g]>(p^.w+c[b[f]])) then
30                                                         begin
31                                                                 c[p^.g]:=p^.w+c[b[f]];
32                                                                 b[r]:=p^.g;
33                                                                 inc(r);
34                                                         end;
35                                                 p:=p^.next;
36                                         end;
37                                 inc(f);
38                         end;
39                 for i:=1 to n do dec(c[i]);
40         end;
41 begin
42         readln(n,m);
43         for i:=1 to n do a[i]:=nil;
44         for i:=1 to m do
45                 begin
46                         readln(j,k);
47                         add(j,k,1);add(k,j,1);
48                 end;
49         spfa(1);
50         for i:=1 to n do
51                 case c[i] of
52                         -1:writeln(1, ---> ,i, : ,Unavailable);
53                         else writeln(1, ---> ,i, : ,c[i]);
54                 end;
55         readln; 
56 end.

3.bat对拍小程序

(PS:由于Bellman-Ford算法具有超高的时空浪费量,还有Floyd一般不用于单源最短路,所以只准备这些)

还有:这次采用的对拍模式如下——模拟一般OI赛制上的10组数据,30%数据满足规模为N<=10000 M<=100000;60%的数据满足规模为N<=30000 M<=200000;100%的数据满足N<=50000 M<=1000000。每10组这样的数据为一组,多组测试

公布结果——在30%的数据时,spfa用时200ms多点,dijkstra用时300ms多点

在60%数据时,spfa用时400ms多点,dijkstra用时1500-1800ms

在100%数据时,dpfa用时1300ms+,dijkstra用时11000-14000ms

差距就是——10倍,尤其是数量上去之后

原因——dijkstra里面最怕的就是一边接着一遍的扫描最小值,而且事实证明,如果像我原来预想的样子写堆优化的话——1.堆优化难写,因为不仅仅涉及到删除和加入新值 2.代码量会成倍的扩大。也就是说真正的成了O(N^2).而spfa是与边的密度相关的,且减少了许多的松弛操作

总结:事实的效果才能说明一切。更何况这个里面是随机生成的数据而不是OI的时候有意构造出来的更加强的数据。。。

(附:用于对拍的batch)

 1 @echo off
 2 :2
 3 set /a s=0
 4 cls
 5 :1
 6 set /a s=s+1
 7 echo Test %s%
 8 if %s% leq 3 (
 9 echo 10000 100000|fuckpath.exe >path.in
10 goto 4)
11 if %s% leq 6 (
12 echo 30000 200000|fuckpath.exe >path.in
13 goto 4)
14 if %s% leq 10 (
15 echo 50000 1000000|fuckpath.exe >path.in
16 goto 4)
17 :4
18 echo.|time
19 type path.in|spfa.exe >spfa.out
20 echo.|time
21 echo.|time
22 type path.in|dijkstra.exe >dijkstra.out
23 echo.|time
24 fc dijkstra.out spfa.out
25 if %s%==10 (goto 3)
26 goto 1
27 :3
28 pause
29 goto 2

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。