学了多年的算法,最短路问题相当之常见————
好久没写过最短路的问题了,直到昨天闲的无聊来了一题——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: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 dijkstra(x:longint);
24 var i,j,k,l:longint;p:point;
25 begin
26 fillchar(c,sizeof(c),0);
27 fillchar(b,sizeof(b),0);
28 c[x]:=1;
29 p:=a[x];
30 while p<>nil do
31 begin
32 if (b[p^.g]=0) and (c[p^.g]=0) then b[p^.g]:=p^.w else b[p^.g]:=min(b[p^.g],p^.w);
33 p:=p^.next;
34 end;
35
36 for i:=1 to n-1 do
37 begin
38 l:=maxlongint;k:=-1;
39 for j:=1 to n do
40 if (c[j]=0) and (b[j]>0) and (b[j]<l) then
41 begin
42 l:=b[j];k:=j;
43 end;
44 if k=-1 then break;
45 c[k]:=1;p:=a[k];
46 while p<>nil do
47 begin
48 if (c[p^.g]=0) and ((b[p^.g]=0) or (b[p^.g]>(p^.w+l))) then b[p^.g]:=p^.w+l;
49 p:=p^.next;
50 end;
51 end;
52 end;
53 begin
54 readln(n,m);
55 for i:=1 to n do a[i]:=nil;
56 for i:=1 to m do
57 begin
58 readln(j,k,l);
59 add(j,k,l);
60 end;
61 dijkstra(1);
62 for i:=1 to n do
63 case c[i] of
64 1:writeln(1,' ---> ',i,' : ',b[i]);
65 0:writeln(1,' ---> ',i,' : ','Unavailable');
66 end;
67 readln;
68 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