AcWing 3760. 最大剩余油量(树的最长路径)

题目

一个国家由 n 个城市组成,这 n 个城市由 n−1 条双向道路连接,呈一个树形结构。

每个城市都设有加油站,在第 i 个城市可以购买 wi 升汽油。

汽车在道路上行驶,毫无疑问也会消耗汽油,每条道路的具体耗油量也会给出。

现在,需要制定一条汽车的行进路线,从任意城市 s 出发,经过一条简单路径,到达任意城市 e 结束。

注意,行进路线也可以只包含一个城市(也就是哪都没去)。

汽车初始时油箱是空的,但是可以在路线中经过的每个城市购买汽油,包括开始城市和最终城市。

如果在一条行进路线中,汽车沿一条道路从某一城市开往另一城市时,现有油量小于该条道路所需油量,那么就说明这条行进路线行不通。

请问,在保证行进路线合理的情况下,汽车在抵达最终城市后,可以剩余的最大油量是多少?

再次提醒,汽车在最终城市也可以加油。

输入输出

输入:第一行包含整数 n。

第二行包含 n 个整数 w1,w2,…,wn。

接下来 n−1 行,每行包含三个整数 u,v,c,表示城市 u 和城市 v 之间存在一条双向道路,耗油量为 c。
输出:一个整数,表示可能的最大剩余油量。

思路

这道题是选出一条路径使最大剩余油量最大,即树的最长路径(树的直径)问题。点权为正,边权为负。
可以枚举一条路径的最高点,该点的最长路径由根节点的点权+子树的最长长度+子树的次长长度。用递归来做,递归返回的是子树往下的最长长度,计算出后,更新最长路径长度。写的太混乱了

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 300010;
typedef long long LL;

int tot;
int head[N << 1],ver[N << 1], nxt[N << 1],edge[N << 1];
int w[N];
LL ans;
void add(int u, int v, int c)  // 添加一条边a->b
{
    ver[++tot] = v; nxt[tot] = head[u]; head[u] = tot; edge[tot] = c;
}

LL dfs(int u,int fa){
    LL max1=0,max2=0;   //以u为根节点的子树中最大的长度和次大的长度
    for(int i = head[u];i;i = nxt[i]){
        int v = ver[i];
        if(v == fa) continue;
        LL d = dfs(v,u);    //d是以v为根节点的子树的最长路径
        if(d < edge[i]) continue;
        d -= edge[i];  //走子树到根节点需要耗费油量
        if(d >= max1) max2 = max1,max1 = d; //先更新max2,再更新max1
        else if(d > max2) max2 = d;
    }
    ans = max(ans, max1 + max2 + w[u]);
    return max1 + w[u]; //返回的是以u为根节点的最长路径
}

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++ )  cin >> w[i];
    for (int i = 0; i < n-1; i ++ ){
        int u,v,c;
        cin >> u >> v >> c;
        add(u, v, c);
        add(v, u, c);
    }
    dfs(1,0);
    cout << ans;
}

posted @ 2021-07-18 13:42  inss!w!  阅读(38)  评论(0编辑  收藏  举报