题目地址:
https://www.acwing.com/problem/content/description/3763/
一个国家由 n n n个城市组成,这 n n n个城市由 n − 1 n−1 n−1条双向道路连接,呈一个树形结构。每个城市都设有加油站,在第 i i i个城市可以购买 w i w_i wi升汽油。汽车在道路上行驶,毫无疑问也会消耗汽油,每条道路的具体耗油量也会给出。现在,需要制定一条汽车的行进路线,从任意城市 s s s出发,经过一条简单路径,到达任意城市 e e e结束。注意,行进路线也可以只包含一个城市(也就是哪都没去)。汽车初始时油箱是空的,但是可以在路线中经过的每个城市购买汽油,包括开始城市和最终城市。如果在一条行进路线中,汽车沿一条道路从某一城市开往另一城市时,现有油量小于该条道路所需油量,那么就说明这条行进路线行不通。请问,在保证行进路线合理的情况下,汽车在抵达最终城市后,可以剩余的最大油量是多少?再次提醒,汽车在最终城市也可以加油。
输入格式:
第一行包含整数
n
n
n。第二行包含
n
n
n个整数
w
1
,
w
2
,
…
,
w
n
w_1,w_2,…,w_n
w1,w2,…,wn。接下来
n
−
1
n−1
n−1行,每行包含三个整数
u
,
v
,
c
u,v,c
u,v,c,表示城市
u
u
u和城市
v
v
v之间存在一条双向道路,耗油量为
c
c
c。
输出格式:
一个整数,表示可能的最大剩余油量。
数据范围:
前三个测试点满足,
1
≤
n
≤
5
1≤n≤5
1≤n≤5。所有测试点满足,
1
≤
n
≤
3
×
1
0
5
1≤n≤3×10^5
1≤n≤3×105,
0
≤
w
i
≤
1
0
9
0≤w_i≤10^9
0≤wi≤109,
1
≤
u
,
v
≤
n
1≤u,v≤n
1≤u,v≤n,
1
≤
c
≤
1
0
9
1≤c≤10^9
1≤c≤109,
u
≠
v
u≠v
u=v。
可以以 1 1 1号点为树根做DFS,枚举以每个点 r r r为最高点的所有路径,求剩余油量最大是多少。那么可以枚举 r r r的所有的孩子,例如对于孩子 x x x,递归地计算从 x x x向下走走到某个点,能最多剩余多少油量。由于路径是对称的,所以从 x x x向下走到某个点能最多剩余多少油量,就等于从某个点向上走到 x x x能最多剩余的油量。如果走到 x x x最多的剩余油量不足以再向上走到 r r r,则略过该孩子,说明不存在经过 x x x到 r r r的合法路径。这样枚举完所有孩子之后,取剩余油量最大和次大的两个孩子,和 r r r一起连成一个通路,这个路径就是以 r r r为最高点且剩余油量最多的路径。如果用公式来说明的话,设 f [ r ] f[r] f[r]是从 r r r向下走,走到某个点能剩余的最多油量,则有: f [ r ] = max r → x { f [ x ] } + o [ r ] f[r]=\max_{r\to x}\{f[x]\}+o[r] f[r]=r→xmax{f[x]}+o[r]DFS要返回的就是这个 f f f值。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 3e5 + 10, M = 2 * N;
int n;
// 存每个顶点的存储的油量
int oil[N];
int h[N], e[M], w[M], ne[M], idx;
long res;
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
long dfs(int u, int parent) {
long d1 = 0, d2 = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == parent) continue;
// 递归求解从v向下走能最多剩余多少油量,其就等于
// 从某个点向上走到v能剩余的最多油量
long d = dfs(v, u);
// 如果这个油量不足以支持走到u,则该路径无效,略过之
if (d < w[i]) continue;
// 执行d -= w[i]后,d里存的就是从u向下走到v的所有路径里的剩余最大油量
d -= w[i];
// 求剩余油量前二大的孩子的油量
if (d >= d1) d2 = d1, d1 = d;
else if (d > d2) d2 = d;
}
res = max(res, d1 + d2 + oil[u]);
return d1 + oil[u];
}
int main() {
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++) scanf("%d", &oil[i]);
for (int i = 0; i < n - 1; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
dfs(1, -1);
printf("%ld\n", res);
return 0;
}
时空复杂度 O ( n ) O(n) O(n)。