1. 程式人生 > 其它 >AcWing 1220. 生命之樹

AcWing 1220. 生命之樹

題目連結


題目描述:

在X森林裡,上帝建立了生命之樹。
他給每棵樹的每個節點(葉子也稱為一個節點)上,都標了一個整數,代表這個點的和諧值。
上帝要在這棵樹內選出一個非空節點集 S,使得對於 S 中的任意兩個點 a,b,都存在一個點列 {a,v1,v2,…,vk,b} 使得這個點列中的每個點都是 S 裡面的元素,且序列中相鄰兩個點間有一條邊相連。
在這個前提下,上帝要使得 S 中的點所對應的整數的和儘量大。
這個最大的和就是上帝給生命之樹的評分。
經過 atm 的努力,他已經知道了上帝給每棵樹上每個節點上的整數。
但是由於 atm 不擅長計算,他不知道怎樣有效的求評分。
他需要你為他寫一個程式來計算一棵樹的分數。


題目大意:求一個樹中連通塊和的最大值。

解決方法:樹形DP

f[u]陣列:在以u為根的子樹中包含u的所有連通塊的權值的最大值。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010, M = N * 2;

int n;
int w[N];
int h[N], e[M], ne[M], idx;
LL f[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u, int father)
{
    f[u] = w[u];
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j != father)
        {
            dfs(j, u);
            f[u] += max(0ll, f[j]);
        }
    }
}

int main()
{
    scanf("%d", &n);
    memset(h, -1, sizeof h);

    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    dfs(1, -1);

    LL res = f[1];
    for (int i = 2; i <= n; i ++ ) res = max(res, f[i]);

    printf("%lld\n", res);

    return 0;
}