没有上司的舞会(树形dp)

题目描述

Ural大学有N名职员,编号为1~N。
他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。
每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。
现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。
在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。
输入格式
第一行一个整数N。

接下来N行,第 i 行表示 i 号职员的快乐指数Hi。

接下来N-1行,每行输入一对整数L, K,表示K是L的直接上司。

输出格式
输出最大的快乐指数。

数据范围

1 ≤ N ≤ 6000,
−128 ≤ Hi ≤ 127

输入样例:

7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5

输出样例:

5

思路

这个题目是树形dp的题目,所谓树形dp,就是在一棵树上进行,没有环,dfs不会重复。
首先就是将这个边的关系用邻接表存起来。
题目要求就是不能和直接上级一起,要么没有自己,要么没有直接上级,就相当于咱们的学生、班主任和级部主任一样,要么有学生没有班主任,要么由班主任没有学生。

假如选择了学生,班主任不能去,但是级部主任可以去。

既然是一道动态规划的问题,我们就要从大问题归结为小问题。
在这里插入图片描述
首先,我们来定义一个数组dp[ i ] [2]
dp[i][0] 的含义就是从以 i 为根的子树中选择,并且选 i 这个结点的方案。
dp[i][1] 的含义就是从以 i 为根的子树中选择,并且不选 i 这个结点的方案。

  1. 选择了 5 ,那么结点 3 、4 就不能选择,那么当前的快乐指数dp[ 5 ][ 1 ] = 5;
  2. 不选择了 5 ,那么结点 3 、4 就能选择,结点1、2、3、4就不能选择,那么当前的快乐指数为dp[ 5 ][ 0 ] = 2;
  3. 所以快乐指数最多为max(dp[i][0],dp[i][1])
  4. 将子结点的最大快乐指数传到父节点,然后在传到根节点root,我们最终就是要求max(dp[root][1],dp[root][0])
细节
  1. 我们要找到根节点root,我们可以开一个数组布尔类型的数组isfa[i],如果当前结点 i 有父节点,那么 isfa[i]=true
  2. 将边的关系用邻接表存起来。
  3. 在树上进行递归搜索
代码
#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 6010;

int n,root;  //root 为根节点
int dp[N][2];// 为当前结点的最大幸福指数
int val[N],isfa[N];//isfa[N]为当前结点是否有父节点
int h[N],e[N],ne[N],idx;// 储存邻接表

void add(int a,int b)// 加一条边a->b
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a] = idx ++;
}

void dfs(int u)
{
    dp[u][1] = val[u];//如果选择当前结点,要将当前结点的幸福指数加上
    
    for(int i = h[u];i != -1;i = ne[i])
    {
        int j = e[i];
        
        dfs(j);//对子结点进行搜索

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

void Findroot()
{
    root=1;
    while(isfa[root])
        root++;
}

int main()
{
    scanf("%d",&n);
    
    for(int i=1;i<=n;i++)
        scanf("%d",val+i);
    
    memset(h,-1,sizeof h);// 初始化头结点
    
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        isfa[a]=1;
        add(b,a);
    }
    
    Findroot();//寻找根节点
    
    dfs(root);
    
    printf("%d\n",max(dp[root][0],dp[root][1]));
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值