洛谷 P1462 通往奥格瑞玛的道路

题目

在艾泽拉斯,有n个城市。编号为1,2,3,…,n。

城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

假设1为暴风城,n为奥格瑞玛,而他的血量最多为b,出发时他的血量是满的。

歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

对于60%的数据,满足n≤200,m≤10000,b≤200
对于100%的数据,满足n≤10000,m≤50000,b≤1000000000
对于100%的数据,满足ci≤1000000000,fi≤1000000000,可能有两条边连接着相同的城市。

题解

歪嘴哦这个倒霉的孩子居然同时具有血量和过路费,两个代价,怎么办?于是考虑先确定一个值。确定过路费后,发现剩下的问题就是最短路,于是果断SPFA

那么怎么确定过路费呢?
考虑到题目要求“他所经过的所有城市中最多的一次收取的费用的最小值是多少”,于是可以看成求最大值里的最小值。这个嘛,最大值里的最小值,最小值里的最大值,统统果断二分。
二分答案,然后用SPFA判断当前答案是否行得通。

然而提交了

24
次都没有对

最后发现错的是什么地方呢,读入!
这个故事告诉我们哪怕是读入也可以让你的正确率暴跌

时间复杂度O(m log n)

代码

var
  n,m,b,i,j,k,l,r,mid:longint;
  a:array[1..100000,1..4]of longint;
  s,w,c:array[0..10000]of longint;
  ls:array[1..100000]of longint;
  v:array[1..1000000]of longint;
  d:array[1..50000]of boolean;

function spfa(key:longint):boolean;
var
  l,r,i,j,k,t:longint;
begin
  for i:=1 to n do
    s[i]:=1000000000;
  fillchar(d,sizeof(d),true);
  l:=0;r:=1;v[r]:=1;s[1]:=0;d[1]:=false;
  while l<r do
    begin
      inc(l);
      i:=ls[v[l]];
      while i>0 do
        begin
          if c[a[i,1]]>key then
            begin
              i:=a[i,4];
              continue;
            end;
          if (s[a[i,1]]+a[i,3]<=b)and(s[a[i,1]]+a[i,3]<=s[a[i,2]]) then
            begin
              s[a[i,2]]:=s[a[i,1]]+a[i,3];
              if d[a[i,2]] then
                begin
                  inc(r);
                  v[r]:=a[i,2];
                  d[a[i,2]]:=false;
                end;
            end;
          i:=a[i,4];
        end;
      d[v[l]]:=true;
    end;
  if s[n]<=b then exit(true) else exit(false);
end;

procedure qsort(l,r:longint);
var
  i,j,key,t:longint;
begin
  if l>=r then exit;
  i:=l;j:=r;
  key:=w[(l+r) div 2];
  repeat
    while w[i]<key do inc(i);
    while w[j]>key do dec(j);
    if i<=j then
      begin
        t:=w[i];w[i]:=w[j];w[j]:=t;
        inc(i);dec(j);
      end;
  until i>j;
  qsort(i,r);
  qsort(l,j);
end;

begin
  readln(n,m,b);
  for i:=1 to n do
    begin
      readln(w[i]);
      if w[i]>j then begin j:=w[i];r:=i;end;
    end;
  k:=w[1];if w[n]>k then k:=w[n];
  c:=w;
  qsort(1,n);
  j:=0;
  for i:=1 to m do
    begin
      inc(j);
      readln(a[j,1],a[j,2],a[j,3]);
          a[j,4]:=ls[a[j,1]];ls[a[j,1]]:=j;
          inc(j);
          a[j,1]:=a[j-1,2];a[j,2]:=a[j-1,1];a[j,3]:=a[j-1,3];
          a[j,4]:=ls[a[j-1,2]];ls[a[j-1,2]]:=j;
    end;
  if not(spfa(w[n])) then begin writeln('AFK');halt;end;
  l:=1;r:=n;
  while l<r do
    begin
      mid:=(l+r) div 2;
      if (k>w[mid]) or not(spfa(w[mid])) then l:=mid+1 else r:=mid;
    end;
  if spfa(w[l]) then
  writeln(w[l])
  else writeln(w[r]);
end.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值