luogu P1462 通往奥格瑞玛的道路 题解

发布于 2023-07-02  320 次阅读


通往奥格瑞玛的道路

题目背景

在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。

有一天他醒来后发现自己居然到了联盟的主城暴风城。

在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。

题目描述

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

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

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

假设 $1$ 为暴风城,$n$ 为奥格瑞玛,而他的血量最多为 $b$,出发时他的血量是满的。如果他的血量降低至负数,则他就无法到达奥格瑞玛。

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

输入格式

第一行 $3$ 个正整数,$n,m,b$。分别表示有 $n$ 个城市,$m$ 条公路,歪嘴哦的血量为 $b$。

接下来有 $n$ 行,每行 $1$ 个正整数,$f_i$。表示经过城市 $i$,需要交费 $f_i$ 元。

再接下来有 $m$ 行,每行 $3$ 个正整数,$a_i,b_i,c_i$($1\leq a_i,b_i\leq n$)。表示城市 $a_i$ 和城市 $b_i$ 之间有一条公路,如果从城市 $a_i$ 到城市 $b_i$,或者从城市 $b_i$ 到城市 $a_i$,会损失 $c_i$ 的血量。

输出格式

仅一个整数,表示歪嘴哦交费最多的一次的最小值。

如果他无法到达奥格瑞玛,输出 AFK

样例 #1

样例输入 #1

4 4 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3

样例输出 #1

10

提示

对于 $60\%$ 的数据,满足 $n\leq 200$,$m\leq 10^4$,$b\leq 200$;

对于 $100\%$ 的数据,满足 $1\leq n\leq 10^4$,$1\leq m\leq 5\times 10^4$,$1\leq b\leq 10^9$;

对于 $100\%$ 的数据,满足 $1\leq c_i\leq 10^9$,$1\leq f_i\leq 10^9$,可能有两条边连接着相同的城市。

题解

本题两个条件,一个是金币,一个是血量,根据题目中的所有城市中最多的一次收取的费用的最小值,可以明显看出是一个智障二分,要求经过城市尽量多而且死不了,这个条件其实就是一个裸的最短路,用dij来check二分就行,我们要二分金币的数量,并在更新最短路的时候特判,如果当前要check的金币数小于当前节点,当前节点就不能更新最短路。

Code

import java.io.*;
import java.util.*;

public class Main {
    static int maxn = 100005, inf = 0x3f3f3f3f;

    static class node {
        int to;
        int next;
        int data;
    }

    static node edge[] = new node[maxn];
    static int head[] = new int[maxn], cnt = 0, n, m, b;
    static int dis[] = new int[maxn], f[] = new int[maxn];
    static boolean vis[] = new boolean[maxn];

    static void add_edge(int from, int to, int data) {
        edge[++cnt] = new node();
        edge[cnt].to = to;
        edge[cnt].next = head[from];
        head[from] = cnt;
        edge[cnt].data = data;
    }

    static boolean dijkstra(int x) {
        Arrays.fill(vis, false);
        Arrays.fill(dis, inf);
        dis[1] = 0;
        PriorityQueue<Heap> q = new PriorityQueue<Heap>((a, b) -> a.data - b.data);
        Heap temp = new Heap(1, 0); // 第一次写,写的很丑陋
        q.add(temp);
        while (!q.isEmpty()) {
            int now = q.peek().now;
            q.poll();
            if (vis[now] || f[now] > x) // 金币数特判
                continue;
            vis[now] = true;
            for (int i = head[now]; i != 0; i = edge[i].next) {
                int to = edge[i].to;
                if (dis[to] > dis[now] + edge[i].data && f[to] <= x) { // 金币数特判
                    dis[to] = dis[now] + edge[i].data;
                    temp = new Heap(to, dis[to]);
                    q.add(temp);
                }
            }
        }
        return dis[n] <= b;
    }

    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

    public static void main(String[] args) {

        n = nextInt();
        m = nextInt();
        b = nextInt();

        int maxf = 0;
        for (int i = 1; i <= n; i++) {
            f[i] = nextInt();
            maxf = Math.max(maxf, f[i]);
        }

        for (int i = 1, x, y, z; i <= m; i++) {
            x = nextInt();
            y = nextInt();
            z = nextInt();
            add_edge(x, y, z);
            add_edge(y, x, z);
        }
        int l = 0, r = maxf, ans = -1;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (dijkstra(mid)) {
                r = mid - 1;
                ans = mid;
            } else
                l = mid + 1;
        }
        System.out.println(ans == -1 ? "AFK" : ans);
    }

    static int nextInt() { // 快读
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (int) in.nval;
    }

    static class Heap {
        int now;
        int data;

        public Heap(int now, int data) {
            this.now = now;
            this.data = data;
        }
    }
}

end