没有上司的舞会【树形DP 】

文章介绍了如何使用树形动态规划(DP)算法来解决寻找树结构中最大权值的问题。通过定义f[u][0/1]表示以节点u为根且不选或选择u的最大权值,然后利用递归的深度优先搜索(DFS)策略,分别计算选择父节点和不选择父节点时的最优解,最终得到整个树的最大权值。
摘要由CSDN通过智能技术生成

😊😊 😊😊
不求点赞,只求耐心看完,指出您的疑惑和写的不好的地方,谢谢您。本人会及时更正感谢。希望看完后能帮助您理解算法的本质
😊😊 😊😊

题目描述:

一、本题考察算法:树形DP😊

思路:

在这里插入图片描述

  1. 由题意可知,父子节点之间相连,那么选了父节点,则该父亲的所有子节点都不能选。直接相连的两个节点只能选其中一个.
  2. 对于当前节点是选还是不选,取决于它的左右子树状况,有时候,父节点的权值大于子树节点的权值之和,此时应该选父节点。但是有时父节点的权值小于子树节点的权值之和,此时不选当前节点。
  3. 所以由上述分析我们可以设: f [ u ] [ 0 / 1 ] f[u][0/1] f[u][0/1]:表示以u为根的子树,不选或者选择 u u u 号节点的最大权值。目标求值: m a x ( f [ u ] [ 1 ] , f [ u ] [ 0 ] ) ; max(f[u][1], f[u][0]); max(f[u][1],f[u][0])
  4. 计算 f [ u ] [ 1 / 0 ] f[u][1/0] f[u][1/0]:对于该方程而言,
    选择父节点的话:则选择根节点的最大权值为:
    f [ u ] [ 1 ] = ∑ u = 1 s o n f [ i ] [ 0 ] ,  i 是儿子节点的编号。 f[u][1] = \sum\limits_{u=1}^{son}f[i][0],\ i是儿子节点的编号。 f[u][1]=u=1sonf[i][0] i是儿子节点的编号。
    不选择父节点 u u u:则根节点的最大权值为:
    f [ u ] [ 0 ] = ∑ i = 1 s o n m a x ( f [ i ] [ 1 ] , f [ i ] [ 0 ] ) ; f[u][0] = \sum\limits_{i=1}^{son}max(f[i][1], f[i][0]); f[u][0]=i=1sonmax(f[i][1],f[i][0])
    又因为是在树上,所以对于子树的权值和,同样的计算方式,只不过采用的递归回溯去求解!

代码:


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

using namespace std;

const int N = 6e3 + 10;
int h[N], e[N], ne[N], idx;
int n;
int happy[N];
bool fa[N];
int f[N][2];    //第一维表示节点编号 。第二维表示节点u选还是不选的最大价值 

void add (int a, int b) //a是b的父节点! 
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

//当前子树的根节点:对于第u个节点选还是不选取决于它子树的影响状态:所以需要递归到底! 由底部节点决定上一层选还是不选,比较选和不选的最大价值。 
void dfs (int u) 
{
    f[u][1] = happy[u]; //选第u个节点的话 ,其价值就是该节点的价值 + 子树的价值(该节点的儿子节点不算,算它的儿子节点的子树) 
    for (int i=h[u]; ~i; i = ne[i])
    {
        int v = e[i];
        dfs (v);        //递归到底部!
        f[u][0] += max(f[v][1], f[v][0]);   //父节点 累加子节点!所以是+= 
        f[u][1] += f[v][0];
    }
}

int main()
{
    cin >> n;
    memset(h, -1, sizeof (h));

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

    for (int i=1; i < n; i ++) 
    {
        int a, b;
        cin >> a >> b;  //b是a的父节点!注意这是有向图!而不是无向图!
        add (b, a);
        fa[a] = true;   //表示a号节点有父节点! 
    }
    int root = 1;
    while (fa[root]) root ++;   //此时root是根节点!

    dfs (root); 
    cout << max(f[root][1], f[root][0]) << endl;

    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值