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

题目地址:

https://www.luogu.com.cn/problem/P1462

题目背景:
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。有一天他醒来后发现自己居然到了联盟的主城暴风城。在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。

题目描述:
在艾泽拉斯,有n nn个城市。编号为1 , 2 , 3 , … , n 1,2,3,\ldots,n1,2,3,,n。城市之间有m mm条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。假设1 11为暴风城,n nn为奥格瑞玛,而他的血量最多为b bb,出发时他的血量是满的。如果他的血量降低至负数,则他就无法到达奥格瑞玛。歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

输入格式:
第一行3 33个正整数,n , m , b n,m,bn,m,b。分别表示有n nn个城市,m mm条公路,歪嘴哦的血量为b bb
接下来有n nn行,每行1 11个正整数,f i f_ifi。表示经过城市i ii,需要交费f i f_ifi元。
再接下来有m mm行,每行3 33个正整数,a i , b i , c i a_i,b_i,c_iai,bi,ci1 ≤ a i , b i ≤ n 1\leq a_i,b_i\leq n1ai,bin)。表示城市a i a_iai和城市b i b_ibi之间有一条公路,如果从城市a i a_iai到城市b i b_ibi,或者从城市b i b_ibi到城市a i a_iai,会损失c i c_ici的血量。

输出格式:
仅一个整数,表示歪嘴哦交费最多的一次的最小值。如果他无法到达奥格瑞玛,输出AFK

数据范围:
对于60 % 60\%60%的数据,满足n ≤ 200 n\leq 200n200m ≤ 1 0 4 m\leq 10^4m104b ≤ 200 b\leq 200b200
对于100 % 100\%100%的数据,满足n ≤ 1 0 4 n\leq 10^4n104m ≤ 5 × 1 0 4 m\leq 5\times 10^4m5×104b ≤ 1 0 9 b\leq 10^9b109
对于100 % 100\%100%的数据,满足c i ≤ 1 0 9 c_i\leq 10^9ci109f i ≤ 1 0 9 f_i\leq 10^9fi109,可能有两条边连接着相同的城市。

思路是二分答案 + 最短路。本题是要求在保证血量不变为负的前提下,单次最大收费的最小值可以是多少。容易知道,对于单次最大收费小于等于x xx的情形如果存在方案,那么对于更大的x xx当然也存在方案。从而我们可以用二分。由于起点和终点显然是要收费的,所以二分左端点为max ⁡ { f 1 , f n } \max\{f_1,f_n\}max{f1,fn};单次最大收费的最大可能即为max ⁡ i f i \max_if_imaxifi,此为右端点。对于每个位于区间内的x xx,我们判断只走收费不超过x xx的城市,是否能从1 11走到n nn,也即判断从1 11走到n nn,如果将耗血量视为费用,最短路长度是否小于等于b bb,可以用Dijkstra算法来做。如果能,则收缩右端点;否则收缩左端点。代码如下:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
using PLI = pair<long, int>;

const int N = 10010, M = 100010;
int n, m, b, fee[N];
int h[N], e[M], ne[M], w[M], idx;
long dist[N];
bool vis[N];

#define add(a, b, c) \
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++

int dijkstra(int maxf) {
  memset(dist, 0x3f, sizeof dist);
  memset(vis, 0, sizeof vis);
  dist[1] = 0;
  priority_queue<PLI, vector<PLI>, greater<PLI>> heap;
  heap.push({0, 1});
  while (heap.size()) {
    auto t = heap.top(); heap.pop();
    int u = t.second;
    if (vis[u]) continue;
    if (u == n) break;
    vis[u] = true;
    for (int i = h[u]; ~i; i = ne[i]) {
      int v = e[i];
      if (fee[v] > maxf) continue;
      if (dist[u] + w[i] < dist[v]) {
        dist[v] = dist[u] + w[i];
        heap.push({dist[v], v});
      }
    }
  }

  return dist[n];
}

int main() {
  memset(h, -1, sizeof h);
  scanf("%d%d%d", &n, &m, &b);
  int l = 0, r = 0;
  for (int i = 1; i <= n; i++) {
    scanf("%d", &fee[i]);
    r = max(r, fee[i]);
  }
  l = max(fee[1], fee[n]);
  for (int i = 1; i <= m; i++) {
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    add(a, b, c), add(b, a, c);
  }

  while (l < r) {
    int mid = l + (r - l >> 1);
    if (dijkstra(mid) <= b) r = mid;
    else l = mid + 1;
  }

  if (dijkstra(l) > b) puts("AFK");
  else printf("%d\n", l);
}

时间复杂度O ( m log ⁡ n log ⁡ ( R − L ) ) O(m\log n\log (R-L))O(mlognlog(RL))L = max ⁡ { f 1 , f n } , R = max ⁡ i f i L=\max\{f_1,f_n\}, R=\max_i f_iL=max{f1,fn},R=maxifi,空间O ( n + m ) O(n+m)O(n+m)


版权声明:本文为qq_46105170原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。