动态规划——树形dp


树形动态规划(Tree DP)是动态规划(DP)的一种特殊形式,它专门用于解决树形结构上的问题。在树形DP中,我们通常使用递归的方法来遍历树的节点,并在每个节点上计算一些基于其子节点信息的值。
凡是牵涉到树的题目,一般都会用到dfs(爆搜),或者bfs(宽搜)。本题会用到dfs,忘记的话可以向前复习:DFS

题目:285. 没有上司的舞会 - AcWing题库

题目含义:

    在不选择其直接上司的前提下求最大的happy值,转化到图上:
        若选择根节点5,则4和3不能选(因为5是4、3的直接父节点),6、7、1、2可以选(因为5不是6、7、1、2的直接父节点)。
        若选择了父节点4,那么5不能选(5是4的直接父节点);6、7不能选(4是6、7的直接父节点)......

 思路:

        状态表示:集合:①f[u][0]所有从以u为根的子树中选择,并且不选u这个点的方案
                                     ②f[u][1]所有从以u为根的子树中选择,并且选择u这个点的方案
                          属性:MAX
        状态计算:当集合为①时,不选择u这个根节点,那么根节点u的所有子节点可以选or不选,取一个最大值即可:f[u][0] += Σmax(f[j][1],f[j][0])
                          当集合为②时,选择u这个根节点,那么根节点u的所有子节点都不能选,那么f[u][1] += Σf[j][0]。
          遍历每一个根节点的所有子节点时通常采用爆搜的方式(dfs),我们在dfs中处理根节点是,往往是先处理好与根节点所有相连的子节点,而每一个子节点的状态即为f[ j ][0],f[ j ][1]。
一开始从根节点进行向下爆搜,而根节点无上司,所以找到一个没有上司的点即为根节点

代码:
 

#include<bits/stdc++.h>

using namespace std;

const int N = 6010;
int n;
int happy[N];//每一个人的高兴度
int f[N][2];//选or不选的状态
int idx, e[N], ne[N], h[N];//图——注意本题是有向图,所以不存在双向指向。所以不需要额外的st数组判断是否被遍历过
bool whether_st[N];//寻找根节点的bool数组

void add(int a, int b)//b加到a的后面
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
    f[u][1] = happy[u];//选择u作为根节点时,它的基础happy值为happy[u]
    
    for(int i = h[u]; i != -1; i = ne[i] )
    {
        int j = e[i];
        dfs(j);//爆搜根节点u的所有子节点
        
        f[u][1] += f[j][0];//选根节点u
        f[u][0] += max(f[j][0], f[j][1]);//不选根节点u
    }
    
    return;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ ) cin >> happy[i];

    memset(h, -1, sizeof h);
    n -= 1;
    while(n -- )
    {
        int a, b;
        cin >> a >> b;
        add(b, a);//子节点a应该在父节点b之后
        whether_st[a] = true;//说明a有上司
    }
    
    //找根节点
    int root = 1;
    while(whether_st[root]) root ++ ;
            
            
    //从根节点开始遍历的原因是因为每一个点只会被遍历一次,即爆搜到底
    dfs(root);
    //输出状态①和②的最大值即可
    cout << max(f[root][1], f[root][0]) << endl;
    
    return 0;
    
}

本题即是简单的dp+dfs处理+树的建立。不过合在一块,若有一个部分卡住,那么可能就不简单了😂

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值