市选快到了,尽管我能预测到自己有99.9%的几率要挂(flag),我还是决定好好复习一下,于是找了一道SPFA开切。
SPFA就是一个求最短路的算法,其中心思想就是用一个队列来存要扩展的节点,每次从队首元素进行扩展,如果有一点的最短路径值发生变化,那么就把这个点丢进队尾。由于不能让一些点进队次数太多,于是我们再加一个数组进行判重。当然,如果要判图里是否有负环的话,那么可以对每个点的进队次数进行判断,如果一个点的进队次数>图中点的数目,那么图中就一定有负环。
好了,接下来看个题:洛古P1462(话说现在打卡只能在洛古V3.0进行了……)
题目链接:http://dev.luogu.org:3308/problem/show?pid=1462
题目里面出现了一个“交费最多的一次的最小值”,最大值最小,果断二分……
由于这个倒霉的家伙还有一个“血量”,而且还过一条路就扣一些,于是我们想到了在二分过程中SPFA,如果这个人只花mid=(l+r)div 2(初始时r=max(cost[i]))的钱能顺利到终点而且扣的血<=血量的话,那么说明可以少花一点钱,此时r=mid,否则就说明还是得破财消灾,l=mid+1,如此往复直至l=r,取此时的l值再SPFA一次,如果还不能正常到达终点,那么就没办法了,输出AFK,否则输出l即可。
代码如下(当然还是pascal的):
type data=record
v,next:longint;len:int64;
end;
var edge:array[1..60000]of data;
cost:array[1..15000]of longint;
head:array[1..60000]of longint;
d:array[1..60000]of int64;
vis:array[1..20000]of boolean;
queue:array[1..1000000]of longint;
n,m,i,j,x,y,num,l,r,mid:longint;
z,b:int64;
function spfa(lim:longint):boolean;
var he,ta:longint;
i,j,s,t,e:longint;
begin
he:=0;
ta:=1;
queue[1]:=1;
vis[1]:=true;
for i:=1 to n do
d[i]:=10000000000000;
d[1]:=0;
while he<ta do
begin
inc(he);
s:=queue[he];
e:=head[s];
while e<>0 do
begin
t:=edge[e].v;
if cost[t]>lim then
begin
e:=edge[e].next;
continue;
end;
if d[t]>d[s]+edge[e].len then
begin
d[t]:=d[s]+edge[e].len;
if not vis[t] then
begin
inc(ta);
queue[ta]:=t;
vis[t]:=true;
end;
end;
e:=edge[e].next;
end;
vis[s]:=false;
end;
if d[n]>b then exit(false) else exit(true);
end;
procedure addedge(x,y,z:longint);
begin
inc(num);
edge[num].v:=y;
edge[num].next:=head[x];
head[x]:=num;
edge[num].len:=z;
end;
begin
readln(n,m,b);
num:=0;
r:=0;
for i:=1 to n do
begin
readln(cost[i]);
if cost[i]>r then r:=cost[i];
end;
for i:=1 to m do
begin
readln(x,y,z);
addedge(x,y,z);
addedge(y,x,z);
end;
l:=1;
while l<r do
begin
mid:=(l+r)div 2;
if (not spfa(mid))or(cost[1]>mid) then l:=mid+1 else r:=mid;
end;
if spfa(l) then writeln(l) else writeln('AFK');
end.