算法复习——SPFA

市选快到了,尽管我能预测到自己有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.


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值