【ACWing】3760. 最大剩余油量

题目地址:

https://www.acwing.com/problem/content/description/3763/

一个国家由 n n n个城市组成,这 n n n个城市由 n − 1 n−1 n1条双向道路连接,呈一个树形结构。每个城市都设有加油站,在第 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 n1行,每行包含三个整数 u , v , c u,v,c u,v,c,表示城市 u u u和城市 v v v之间存在一条双向道路,耗油量为 c c c

输出格式:
一个整数,表示可能的最大剩余油量。

数据范围:
前三个测试点满足, 1 ≤ n ≤ 5 1≤n≤5 1n5。所有测试点满足, 1 ≤ n ≤ 3 × 1 0 5 1≤n≤3×10^5 1n3×105 0 ≤ w i ≤ 1 0 9 0≤w_i≤10^9 0wi109 1 ≤ u , v ≤ n 1≤u,v≤n 1u,vn 1 ≤ c ≤ 1 0 9 1≤c≤10^9 1c109 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]=rxmax{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)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值