通往奥格瑞玛的道路
题目背景
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。
有一天他醒来后发现自己居然到了联盟的主城暴风城。
在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。
题目描述
在艾泽拉斯,有 $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;
}
}
}
Comments NOTHING