题目模板:最短路
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.