有一个问题我看错了题面,不会做,问Anque,却发现看错的题意却有精妙做法。
题意简述:有一幅n个点,m条边的有向图(2<=n<=100000,1<=m<=200000),求1到n最短路中标号最小的路径。
首先,做S点的最短路,如果dis(x)+d(x,y)=dis(y),则边(x,y)可能在S点出发的最短路上。
这题精妙之处在于如果求出了源点的单源最短路不能保证是标号最小的路径,如果挑可能在最短路上的边走,不一定能走到汇点。
精妙做法就是把边反向,做汇点的单元最短路,从源点走可能在汇点最短路上的边,并且在走的时候,时刻选标号最小的点。因为在源点走可能在汇点最短路上的边必然走的是到汇点的最短路,这时候挑标号最小的点是没有问题的;如果做了源点的最短路,不能保证走可能在源点最短路上的边能走到汇点。
const
mxn=100001;
INF=maxlongint>>1;
type
point=record
y,v,next:longint;
end;
var
map:array[0..500000] of point;
first,dis,q,a:array[0..mxn] of longint;
inque:array[0..mxn] of boolean;
n,m,s,x,y,v,head,tail,t,i:longint;
procedure ins(x,y,v:longint);
begin
inc(s);
map[s].y:=y;
map[s].v:=v;
map[s].next:=first[x];
first[x]:=s;
end;
begin
fillchar(first,sizeof(first),0);s:=1;
readln(n,m);
for i:=1 to m do
begin
read(x,y,v);
ins(x,y,v);
ins(y,x,v);
end;
for i:=1 to n do dis[i]:=INF;
fillchar(inque,sizeof(inque),false);
head:=1;tail:=2;
q[head]:=n;dis[n]:=0;inque[n]:=true;
while head<>tail do
begin
x:=q[head];
t:=first[x];
while t>0 do
begin
y:=map[t].y;
if (t and 1=1)and(dis[x]+map[t].v<dis[y]) then
begin
dis[y]:=dis[x]+map[t].v;
if not inque[y] then
begin
inque[y]:=true;
q[tail]:=y;
inc(tail);if tail>mxn then tail:=1;
end;
end;
t:=map[t].next;
end;
inque[x]:=false;
inc(head);if head>mxn then head:=1;
end;
s:=1;
a[s]:=1;
while a[s]<>n do
begin
x:=a[s];
t:=first[x];
inc(s);a[s]:=INF;
while t>0 do
begin
if (t and 1=0)and(dis[x]-map[t].v=dis[map[t].y])and(map[y].y<a[s])
then a[s]:=map[t].y;
t:=map[t].next;
end;
end;
writeln(dis[1]);
for i:=1 to s-1 do
write(a[i],' ');
writeln(a[s]);
end.