文章目录
1 建议
树形dp套路使用前提:
如果题目求解目标是S
规则,则求解流程可以定成以每一个节点为头节点的子树在S
规则下的每一个答案,并且最终答案一定在其中
[step1]
以某个节点为X
为头节点的子树中,分析答案的可能性(该可能性是以X
左子树、X
右子树和X
整棵树的角度考虑可能性的)
[step2]
根据step1
的可能性分析,枚举出需要的信息
[step3]
合并step2
的信息。对X
左右子树提出同样要求,归纳出信息结构
[step4]
设计出递归函数,递归函数是处理以X
为头节点的情况下的答案,包括设计递归终止条件,默认直接获取X
左右子树的信息,以及具体的递归逻辑,并且返回step3
的信息结构
2 二叉节树节点间的最大距离问题
[问题]
从二叉树的节点a
出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点b
时路径上的节点个数叫作a
到b
的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最大距离。
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_happy
,a,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);
}