学了多年的算法,最短路问题相当之常见————
好久没写过最短路的问题了,直到昨天闲的无聊来了一题——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