[算法入门笔记] 14. 树形dp套路

1 建议

树形dp套路使用前提:

如果题目求解目标是S规则,则求解流程可以定成以每一个节点为头节点的子树在S规则下的每一个答案,并且最终答案一定在其中

[step1]
以某个节点为X为头节点的子树中,分析答案的可能性(该可能性是以X左子树、X右子树和X整棵树的角度考虑可能性的)
[step2]
根据step1的可能性分析,枚举出需要的信息
[step3]
合并step2的信息。对X左右子树提出同样要求,归纳出信息结构
[step4]
设计出递归函数,递归函数是处理以X为头节点的情况下的答案,包括设计递归终止条件,默认直接获取X左右子树的信息,以及具体的递归逻辑,并且返回step3的信息结构

2 二叉节树节点间的最大距离问题

[问题]
从二叉树的节点a出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点b时路径上的节点个数叫作ab的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最大距离

2.1 分析可能性

以某节点X为头节点的子树中,分析可能性,以X的左子树、X的右子树和X整棵树角度考虑

不经过X

  • 可能性1 以X为头节点的子树,最大距离可能是左子树上的最大距离
  • 可能性2 以X为头节点的子树,最大距离可能是右子树上的最大距离

经过X

  • 可能性3 以X为头节点的子树,最大距离可能是从X的左子树离X的最远的节点,先到达X,然后走到X的右子树离X最远的节点,也就是左子树高度+右子树高度+1

2.2 列出信息

根据第一步分析的可能性,列出信息。左子树和右子树需要知道自己这棵树上的最大距离以及高度

2.3 信息汇总

public class ReturnType {
    public int maxDistance;
    public int height;

    public ReturnType(int maxDistance, int height) {
        this.maxDistance = maxDistance;
        this.height = height;
    }
}

2.4 递归逻辑

public ReturnType process(Node x) {
	// 递归终止条件
    if (x == null) {
     return new ReturnType(0,0);
    }
    // 左子树上的最大距离
    ReturnType leftReturnType = process(x.left);
    // 右子树上的最大距离
    ReturnType rightReturnType = process(x.right);

    // 具体递归逻辑
    // 高度信息
    int height = Math.max(leftReturnType.height, rightReturnType.height) + 1;
    // 最大距离信息
    int maxDistance = Math.max(leftReturnType.height + rightReturnType.height + 1,// 经过x节点,取左子树高度+右子树高度+1
            Math.max(leftReturnType.maxDistance, rightReturnType.maxDistance) // 不经过x节点,取左右子树距离最大值
    );
    return new ReturnType(maxDistance, height);
}

在这里插入图片描述

public int getMaxDistance(Node head) {
    return process(head).maxDistance;
}

3 派对最大快乐值

[问题]
定义员工数据结构:

class Employee {
	public int happpy; // 员工的快乐值
	List<Employee> subordinates; // 员工直接下级
}

公司的每个员工都符合Employee类的描述。整个公司的人员结构看成没有环、标准的多叉树,树的头结点为公司唯一的老板。除老板之外的员工都有唯一的直接上级。叶子结点是没有任何下属的基层员工(subordinates域为空),除基层员工外,每个员工都有一个或多个直接下级。
该公司现在举办party,你可以决定哪些员工来,哪些员工不能来,并且遵循:
1.如果某个员工来了,该员工的所有直接下级不能来
2.派对的整体快乐值是所有到场员工快乐值得累加
3.你的目标是使派对整体得快乐值最大
给定一颗多叉树得头结点boss,请返回派对最大快乐值。

3.1 分析可能性

在这里插入图片描述

X为头节点的整棵树,最大快乐值,分为两种

  • 可能性1 X来的情况,整个树最大快乐值为yes_X_max

    X来的情况,快乐值为x_happya,b,c不能来

    假设以a为头节点的整棵树,在a不来的情况下的最大快乐值为no_a_max

    假设以b为头节点的整棵树,在b不来的情况下的最大快乐值为no_b_max

    假设以c为头节点的整棵树,在c不来的情况下的最大快乐值为no_c_max

    y e s X m a x = x H a p p y + n o A m a x + n o B m a x + n o C m a x yesXmax = xHappy+noAmax+noBmax+noCmax yesXmax=xHappy+noAmax+noBmax+noCmax

  • 可能性2 X不来的情况,整棵树最大快乐值为no_X_max

    X 不来的情况,a,b,c谁来不来都可以

    假设以a为头节点的整棵树,在a不来的情况下的最大快乐值为no_a_max,在a来的情况下最大快乐值为yes_a_max

    假设以b为头节点的整棵树,在b不来的情况下的最大快乐值为no_b_max,在b来的情况下最大快乐值为yes_b_max

    假设以c为头节点的整棵树,在c不来的情况下的最大快乐值为no_c_max,在c来的情况下最大快乐值为yes_c_max

    n o X m a x = max ⁡ { n o A m a x , y e s A m a x } + max ⁡ { n o B m a x , y e s B m a x } + max ⁡ { n o C m a x , y e s C m a x } noXmax=\max\{noAmax,yesAmax\}+\max\{noBmax,yesBmax\}+\max\{noCmax,yesCmax\} noXmax=max{noAmax,yesAmax}+max{noBmax,yesBmax}+max{noCmax,yesCmax}

最后,

m a x = max ⁡ { y e s X m a x , n o X m a x } max=\max\{yesXmax,noXmax\} max=max{yesXmax,noXmax}

3.2 列出信息

需要知道该节点来的时候最大快乐值和不来时最大快乐值

3.3 信息汇总

public class ReturnType {
    // 树的头节点来的情况,整棵树最大收益
    public int yesHeadMax;
    // 树的头节点不来的情况,整棵树最大收益
    public int noHeadMax;

    public ReturnType(int yesHeadMax, int noHeadMax) {
        this.yesHeadMax = yesHeadMax;
        this.noHeadMax = noHeadMax;
    }
}

3.4 递归逻辑

// 处理以x为头节点,返回x来和不来的情况下的最大值
public ReturnType process(Employee x) {
    // x来的情况
    int yesX = x.happy;
    // x不来的情况
    int noX = 0;
    // 如果x没有直接下属,直接返回即可
    if (x.subordinates.isEmpty()) {
        return new ReturnType(yesX, noX);
    } else {  // x有直接下属
        // 枚举x的每一个直接下级员工 next
        for (Employee next : x.subordinates) {
            // 递归调用process得到以next为头节点的子树
            // 在next来和不来的情况分别获得的最大收益
            // 处理每个子树的信息
            ReturnType subTreeInfo = process(next);
            // x来,则其直接下属不来
            yesX += subTreeInfo.noHeadMax;
            // x不来
            noX += Math.max(subTreeInfo.yesHeadMax,// 直接下属来
                    subTreeInfo.noHeadMax);// 直接下属不来
        }
    }
    return new ReturnType(yesX, noX);
}
public int getMaxHapply(Employee boss) {
    ReturnType allTreeInfo = process(boss);
    return Math.max(allTreeInfo.noHeadMax, allTreeInfo.yesHeadMax);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cyan Chau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值