树形DP

树形dp

传送门

方法:树形DP

想法

可以把一棵数分割成若干个小子树,在对子树进行分割,你会发现很像dp把大问题化成小问题的性质

而在这道题中,对于一个点,可以选它参加舞会,也可以不选,可以很轻松地想到用dp做

如何dp?

无标题.png

状态计算

沿着想法走,对于一个子树的快乐值,分为两种情况

  1. 参加舞会
  2. 不参加舞会

根据题目,可以简单发现

  1. 选取一个点的时候它的儿子不可选

  2. 不选取的时候儿子可选可不选

f[选][这个点] += f[不选][儿子] + 当前点的快乐值
f[不选][这个点] += max(f[选][儿子], f[不选][儿子])

这个时候我们直接把所有儿子子树的快乐值加起来就是这个点的快乐值了!

时间复杂度

由于我们要遍历每个儿子,儿子数即为边数,因此时间复杂度为:$ O(n) $

码前细节

  1. 用邻接表存储整个图
  2. 题目中没给根节点,所以我们要手动找根节点——用一个数组记录每个点是否有父亲
  3. 记得初始化邻接表的h[]
  4. 输出时要输出根节点选不选两种情况的最大情况

码来!

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 6010;

int happy[N];
int f[2][N];
int e[N], h[N], ne[N], idx;
bool vis[N];

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

int dfs(int u)
{
    f[1][u] = happy[u];

    for(int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];

        dfs(j);

        f[0][u] += max(f[1][j], f[0][j]);
        f[1][u] += f[0][j];
    }
    return 0;
}

int main()
{
    int n;
    scanf("%d", &n);

    for (int i = 1; i <= n; i ++ ) scanf("%d", &happy[i]);
    memset(h, -1, sizeof h);

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

    int root = 1;
    while(vis[root]) root++;

    dfs(root);

    printf("%d", max(f[1][root], f[0][root]));
    return 0;
}
posted @ 2022-07-23 23:05  MoyouSayuki  阅读(28)  评论(0编辑  收藏  举报
:name :name