B-树上子链 牛客小白月赛22(树形DP 最长链)

树上子链

题目链接

题目描述
给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和。
请在树 T 中找出一条最大的子链并输出。
输入描述:
第一行输入一个 n , 1 ≤ n ≤ 1 0 5 n,1 \le n \le 10^5 n,1n105
接下来一行包含 n n n 个数,对于每个数 a i , − 1 0 5 ≤ a i ≤ 1 0 5 a_i, -10^5 \le a_i \le 10^5 ai,105ai105 ,表示 i i i 结点的权值。
接下来有 n-1 行,每一行包含两个数 u , v ( 1 ≤ u , v ≤ n , u ! = v ) u,v(1 \le u,v \le n, u != v) u,v(1u,vn,u!=v),表示 u u u v v v 之间有一条边。
输出描述:
仅包含一个数,表示我们所需要的答案。
输入
5
2 -1 -1 -2 3
1 2
2 3
2 4
2 5
输出
4
说明
样例中最大子链为 1 -> 2 -> 5
备注:
一个结点,也可以称作一条链

思路:
如果这个题没有点的权值,那就是求树的直径。现在多了点的权值,求最长链的大小,其实差不多。
d p u dp_u dpu表示以 u u u 为根的子树中的最长链,答案肯定是以某个点为根的两条子树最长链的和(包括根)。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 1e5 + 5;
const int M = 1e6 + 5;
int n, a[N];
ll dp[N];
vector<int> e[N];
ll ans = -INFF;
void dfs(int u, int fa) {
    dp[u] = a[u];
    for (int v : e[u]) {
        if (v == fa) continue;
        dfs(v, u);
        ans = max(ans, dp[u] + dp[v]); //这里是关键,求了经过u的两条子树上的子链的和的最大值
        dp[u] = max(dp[u], a[u] + dp[v]); //dp[u]表示u到它子树上的一点的最大值
        // 这两句的顺序不能变,注意理解
    }
    ans = max(ans, dp[u]); //最后一个子节点更新
}
int main() {
    #ifdef LZH_LOCAL
    freopen("in.in", "r", stdin);
    // freopen("out.out", "w", stdout);
    #endif
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1, 0);
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值