• AcWing 1220. 生命之树(树形DP)


    在X森林里,上帝创建了生命之树。

    他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。

    上帝要在这棵树内选出一个非空节点集 S,使得对于 S 中的任意两个点 a,b,都存在一个点列 {a,v1,v2,…,vk,b} 使得这个点列中的每个点都是 S 里面的元素,且序列中相邻两个点间有一条边相连。

    在这个前提下,上帝要使得 S 中的点所对应的整数的和尽量大。

    这个最大的和就是上帝给生命之树的评分。

    经过 atm 的努力,他已经知道了上帝给每棵树上每个节点上的整数。

    但是由于 atm 不擅长计算,他不知道怎样有效的求评分。

    他需要你为他写一个程序来计算一棵树的分数。

    输入格式

    第一行一个整数 n 表示这棵树有 n 个节点。

    第二行 n 个整数,依次表示每个节点的评分。

    接下来 n−1 行,每行 2 个整数 u,v,表示存在一条 u 到 v 的边。

    由于这是一棵树,所以是不存在环的。

    树的节点编号从 1 到 n。

    输出格式

    输出一行一个数,表示上帝给这棵树的分数。

    数据范围

    1≤n≤105,
    每个节点的评分的绝对值均不超过 106。

    输入样例:

    5
    1 -2 -3 4 5
    4 2
    3 1
    1 2
    2 5
    

    输出样例:

    8
    

    树形DP。设dp[x]为以x为根的子树里包含x的连通块的最大分数,有转移方程(dp[x] = c[x] + Sigma max(dp[y], 0))

    最后答案对所有dp[i]取max即可。

    代码瞎写的==写麻烦了

    #include <iostream>
    #define int long long
    #define N 100005
    using namespace std;
    int n, tot = 0, head[N], ver[2 * N], Next[2 * N];
    int c[N];
    long long dp[N] = {0}, dp1[N] = {0}; //dp[i]表示以i为根的子树里包含的子联通块的分数最大值 有可能包含i这个点 也可能在i的子树里
    bool in[N] = {0};//in[i] = 1 表示i这个点在i为根的子树里包含的最大分数的联通块内
    void add(int x, int y)
    {
        ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
    }
    long long ans = -1e14;
    void dfs(int x, int pre)
    {
        long long now = -1e14;
        for (int i = head[x]; i; i = Next[i])
        {
            int y = ver[i];
            if(y == pre)
                continue;
            dfs(y, x);
            now = max(now, dp[y]);
        }
        dp[x] = now;
        dp1[x] = c[x];
        if (c[x] >= now)
        {
            dp[x] = c[x];
            in[x] = 1;
        }
        long long tmp = c[x];
        for (int i = head[x]; i; i = Next[i])
        {
            int y = ver[i];
            if(in[y] && dp[y] >= 0)
            {
                tmp += 1ll * dp[y];
            }
            if(dp1[y] >= 0)
                dp1[x] += dp1[y];
        }
    
        if(tmp >= now)
        {
            dp[x] = tmp;
            in[x] = 1;
        }
        ans = max(ans, dp[x]);
        ans = max(ans, dp1[x]);
    }
    signed main()
    {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> c[i];
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            add(u, v);
            add(v, u);
        }
        dfs(1, 0);
        cout << ans;
        return 0;
    }
    
  • 相关阅读:
    bzoj4517 [Sdoi2016]排列计数
    bzoj1415 [Noi2005]聪聪和可可
    两个系列的书
    陈天权:数学分析教学中学到的和想到的
    广义范德蒙德行列式计算
    蒙特卡罗方法入门
    LaTeX 文字带边框
    LaTeX算法排版
    常用工具
    常用算法
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/13957073.html
Copyright © 2020-2023  润新知