题解 P1073 【最优贸易】
twy847727884 · · 题解
我刚开始想得偏简单了,我的想法是:按每个点向其他点进行扩展,扩到就入队,入队就记录前驱,最后把每条路实践一遍。然而事实证明,挂了。
同其他几位大神说的,此题可用两次spfa解决,然而这对最短路渣渣的我来说无疑是一次挑战(本人鶸,大神勿喷),第一次,从1开始走,走到终点,求出min[i],即从1到i的最小进价,然后如法炮制地从n走回来,求出max[i],即从i到n的最大出售价,这个其他几位大神也提到了(然而我居然实现了将近3个小时!!!)。那么,剩下的我在代码里会进行批注
var
i,j,n,m,x,y,z,num,maxn,num2:longint;
a,max,min:array[0..100001] of integer;
que,head,head2:array[0..100001] of longint;
inque:array[0..100001] of boolean;
vet,next,vet2,next2:array[0..500001] of longint;
procedure add(x,y:longint);
begin
inc(num);
next[num]:=head[x];
head[x]:=num;
vet[num]:=y;
end;
procedure add2(x,y:longint);//从n回来的边,从终点走回来是逆向思维,所以路线要相反(这样写的话其实本质就是从i走到n,只是为了spfa是单源最短路而倒过来)
begin
inc(num2);
next2[num2]:=head2[x];
head2[x]:=num2;
vet2[num2]:=y;
end;
procedure spfamin;//1到i的最小进价
var h,t,u,v,e:longint;
begin
fillchar(inque,sizeof(inque),false);
h:=1;t:=1;que[1]:=1;inque[1]:=true;
while h<=t do
begin
u:=que[(h-1) mod n+1];
inc(h);e:=head[u];
inque[u]:=false;
while e<>-1 do
begin
v:=vet[e];
if min[v]>min[u] then
begin
min[v]:=min[u];
if not inque[v] then
begin
inc(t);
que[(t-1) mod n+1]:=v;
inque[v]:=true;
end;
end;
if min[v]>a[v] then//保证每个搜到的点都至少进一次队列
begin
min[v]:=a[v];
if not inque[v] then
begin
inc(t);
que[(t-1) mod n+1]:=v;
inque[v]:=true;
end;
end;
e:=next[e];
end;
end;
end;
procedure spfamax;//i到n(n到i)的最大出售价
var h,t,u,v,e:longint;
begin
fillchar(inque,sizeof(inque),false);
h:=1;t:=1;que[1]:=n;inque[n]:=true;
while h<=t do
begin
u:=que[(h-1) mod n+1];
inc(h);e:=head2[u];
inque[u]:=false;
while e<>-1 do
begin
v:=vet2[e];
if max[v]<max[u] then
begin
max[v]:=max[u];
if not inque[v] then
begin
inc(t);
que[(t-1) mod n+1]:=v;
inque[v]:=true;
end;
end;
if max[v]<a[v] then//保证每个搜到的点都至少进一次队列
begin
max[v]:=a[v];
if not inque[v] then
begin
inc(t);
que[(t-1) mod n+1]:=v;
inque[v]:=true;
end;
end;
e:=next2[e];
end;
end;
end;
begin
read(n,m);
for i:=1 to n do begin read(a[i]);head[i]:=-1;head2[i]:=-1;min[i]:=10000;max[i]:=0;end;
num:=0;num2:=0;
for i:=1 to m do
begin
read(x,y,z);
if z=2 then begin add(x,y);add(y,x);add2(x,y);add2(y,x);end
else begin add(x,y);add2(y,x);end;//注意这里!从终点往回建边的逆向思维实现方法!很容易搞错!
end;
spfamin;spfamax;
maxn:=0;
for i:=1 to n do
if maxn<max[i]-min[i] then maxn:=max[i]-min[i];//求出每个点的差价,算出最大值
write(maxn);//因为初始化是0所以如果都不合算那么也不会赋值
end.