目录
树形DP套路
使用前提
如果题目求解目标是S规则,则求解流程可以定成以每一个节点为头节点的子树在S规则下的每一个答案,并且最终答案一定在其中。
套路详解
树形dp套路第一步: 以某个节点X为头节点的子树中,分析答案有哪些可能性,并且这种分析是以X的左子树、X的右子树和X整棵树的角度来考虑可能性的。
树形dp套路第二步: 根据第一步的可能性分析,列出所有需要的信息。
树形dp套路第三步: 合并第二步的信息,对左树和右树提出同样的要求,并写出信息结构。
树形dp套路第四步: 设计递归函数,递归函数是处理以X为头节点的情况下的答案。包括设计递归的basecase,默认直接得到左树和右树的所有信息,以及把可能性做整合,并且要返回第三步的信息结构这四个小步骤。
树形DP套路正是我们前面介绍的二叉树的递归套路的结构,也就是对于一个二叉树的题目,我可以向左树要信息,也可以向右树要信息,然后求解题目。在这里进行一次更深入的介绍。
树形DP应用
二叉树节点间的最大距离问题
从二叉树的节点a出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点b时路 径上的节点个数叫作a到b的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最大距离。
这个题目完全是按照树形DP的套路,进行分情况分析,树形DP的题目,大多数情况下,都是按照是不是包含头节点进行分类。如果不包含头节点,那么整棵树的最大距离,只能是左树的最大值和右树的最大值中最大的一个。如果包含头节点,那么整棵树最大的距离就是左树的高度加右树的高度加1。那么对这三种情况进行整体分析,我们需要从每棵树得到的信息就是每棵子树的最大具体还有高度。从而按照树形DP的编程套路进行书写即可。
public static class Node {//定义二叉树结构
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static int maxDistance(Node head) {//主函数调用
return process(head).maxDistance;
}
public static class ReturnType{//每一棵子树返回最大距离和高度
public int maxDistance;
public int h;
public ReturnType(int m, int h) {
this.maxDistance = m;;
this.h = h;
}
}
public static ReturnType process(Node head) {//递归过程,以head为头节点整体框架的搭建
if(head == null) {//如果头节点为空,两个值都返回0
return new ReturnType(0,0);
}
ReturnType leftReturnType = process(head.left);//从左树拿信息
ReturnType rightReturnType = process(head.right);//从右树拿信息
int includeHeadDistance = leftReturnType.h + 1 + rightReturnType.h;//包括头节点的最大距离的定义,为左树高度与右树高度的和加1
//不包括头节点的两个情况
int p1 = leftReturnType.maxDistance;//左树的最大距离
int p2 = rightReturnType.maxDistance;//右树的最大距离
int resultDistance = Math.max(Math.max(p1, p2), includeHeadDistance);//最终返回三者中最大的
int hitself = Math.max(leftReturnType.h, leftReturnType.h) + 1;//总树的高度,左树高度和右树高度的最大值加1
return new ReturnType(resultDistance, hitself);//返回总树的最大距离和高度
}
派对的最大快乐值
员工信息的定义如下:
class Employee{
public int happy;//这名员工可以带来的快乐值
List<Employee>subordinates;//这名员工有哪些直接下级
}
公司的每个员工都符合 Employee类的描述.整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。
这个公司现在要办party,你可以决定哪些员工来,哪些员工不来。但是要遵循如下规则:
1.如果某个员工来了,那么这个员工的所有直接下级都不能来。
2.派对的整体快乐值是所有到场员工快乐值的累加。
3.你的目标是让派对的整体快乐值尽量大。
给定一棵多叉树的头节点boss,请返回派对的最大快乐值。
这个问题的解决采用的也是同样的思想,分为包括头节点和不包括头节点两种情况,如果包括头节点,那么它的直接下级不能参加,返回每一棵子树的最大的快乐值加和起来,加上自身的快乐值就是总体的最大快乐值。如果不包括头节点,那么它的直接下级可以参加也可以不参加,返回两者的最大值。从而按照树形DP的套路进行代码的书写。
public static class Employee{
public int happy;
public List<Employee> nexts;
}
public static int maxHappy<Employee boss){
Info headInfo = process(boss);
return Math.max(headInfo.laiMaxHappy,headInfo.buMaxHappy);
}
punlic static class Info{
public int laiMaxHappy;
public int buMaxHappy;
public Info(int lai,int bu){
laiMaxHappy = lai;
buMaxHappy = bu;
}
}
public static Info process(Employee x){
if(x.nexts.isEmpty()){//x是基层员工的时候
return new Info(x.happy,0);
}
int lai = x.happy;//x来的情况下,整棵树的最大收益
int bu = 0;//x不来的情况下,整棵树的最大收益
for(Employee next : x.nexts){
Info nextInfo = process(next);
lai += nextInfo.buMaxHappy;//x来的话,它的直接下级都不能来,后面的来
bu += Math.max(nextInfo.laiMaxHappy,nextInfo.buMaxHappy);//x不来的话,它的直接下级可以选择来不来
}
return new Info(lai,bu);//最终返回来和不来情况的最大值
}