AcWing 285. 没有上司的舞会(入门)

题目链接:点击这里
在这里插入图片描述
在这里插入图片描述

树形DP介绍:

  1. 给定一棵有 N N 个节点的树(通常是无根树,也就是有 N 1 N-1 条无向边),我们可以任选一个节点为根节点,从而定义出每个节点的深度和每棵子树的根。
  2. 在树上设计动态规划算法时,一般就以节点从深到浅(子树从小到大)的顺序作为DP的“阶段”。
  3. DP的状态表示中,第一维通常是节点编号(代表以该节点为根的子树)。
  4. 大多数时候,我们采用递归的方式实现树形动态规划。对于每个节点 x x ,先递归在它的每个子节点上进行DP,在回溯时,从子节点向节点 x x 进行状态转移。

本题思路分析:

以节点编号(子树的根)作为DP状态的第一维。

因为一名职员是否愿意参加只跟他的直接上司是否参加有关,所以我们在每棵子树递归完成时,保留两个“代表信息":

  1. 根节点参加时整棵子树的最大快乐指数和
  2. 根节点不参加时整棵子树的最大快乐指数和

F [ x , 0 ] F[x,0] 表示从以 x x 为根的子树中邀请一部分职员参会, 并且 x x 不参加舞会时,快乐指数总和的最大值。此时, x x 的子节点 s s (直接下属)可以参会,也可以不参会。
F [ x , 0 ] = m a x ( F [ s , 0 ] , F [ s , 1 ] ) F[x,0] = max(F[s, 0], F[s,1])

F [ x , 1 ] F[x,1] 表示从以 x x 为根的子树中邀请一部分职员参会, 并且 x x 参加舞会时,快乐指数总和的最大值。此时, x x 的所有子节点 s s (直接下属)都不能参会。
F [ x , 1 ] = H [ x ] + F [ s , 0 ] F[x,1]= H[x] +F[s, 0]

本题输入的是一棵有根树(指定了节点间的上下属关系),故我们需要先找出没有上司的节点 r o o t root 作为根,DP的目标为 m a x ( F [ r o o t , 0 ] , F [ r o o t , 1 ] ) max(F[root, 0], F[root, 1]) 。时间复杂度 O ( N ) O(N)

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

using namespace std;

const int N = 6010;

int n;

int e[N], w[N], ne[N], h[N], idx;       //数组模拟邻接表
bool st[N];
int f[N][2];


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

void dfs(int u)
{
    f[u][0] = 0;
    f[u][1] = w[u];
    
    for(int i = h[u]; ~i; i = ne[i])     //枚举当前点的所有边
    {
        int j = e[i];
        dfs(j);
        f[u][0] += max(f[j][0], f[j][1]);
        f[u][1] += f[j][0];
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &w[i]);
    
    memset(h, -1, sizeof h);
    for(int i = 1; i <= n - 1; ++i)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(b, a);
        st[a] = true;
    }
    
    int root = 1;
    while(st[root]) root++;     //寻找根节点
    
    dfs(root);
    
    printf("%d", max(f[root][0], f[root][1]));
    
    
    return 0;
}
发布了740 篇原创文章 · 获赞 112 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/104440994
今日推荐