算法学习(22):树形DP补充
树形DP补充
树形dp套路使用前提
如果题目求解目标是S规则,则求解流程可以定成以每一个节点为头节点的子树在S规则下的每一个答案,并且最终答案一定在其中
树形DP的套路步骤
- 以某个节点X为头节点的子树中,分析答案有哪些可能性,并且这种分析是以X的左子树、X的右子树和X整棵树的角度来考虑可能性的
- 根据第一步的可能性分析,列出所有需要的信息
- 合并第二步的信息,对左树和右树提出同样的要求,并写出信息结构
- 设计递归函数,递归函数是处理以X为头节点的情况下的答案。包括设计递归的basecase,默认直接得到左树和右树的所有信息,以及把可能性做整合,并且要返回第三步的信息结构这四个小步骤
常见可能性分类
标准指标头节点参与的和头节点不参与的
题目1:叉树节点间的最大距离问题
从二叉树的节点a出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点b时路径上的节点个数叫作a到b的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最大距离。
解题步骤
- 可能性分析。本题的标准是最大距离,可以按照上面的头节点参与和不参与来讨论
(1)头节点不参与。此时不涉及一个节点跨越头节点从左子树到右子树去的距离,所以这时这棵树的最大距离就是左子树的最大距离和右子树的最大距离两个中更大的那个。
(2)头节点参与。此时就涉及一个节点跨越头节点从左子树到右子树去的距离是最大距离,那么就需要左子树的高度和右子树的高度,此时的最大距离就是左子树高度+1+右子树高度
将两种情况的信息收集起来,返回两种情况中更大的值就是最大距离 - 所需要的信息。需要左右子树的最大距离、左右子树的高度。
- 整合后的信息就是子树的最大距离和高度
- C++代码设计实现
class Info
{
public:
Info(int maxDistance, int height)
{
this->maxDistance = maxDistance;
this->height = height;
}
public:
int maxDistance;
int height;
};
Info process(TreeNode* head);
int maxDistance(TreeNode* head)
{
if (head == NULL)
{
return 0;
}
return process(head).maxDistance;
}
Info process(TreeNode* head)
{
if (head == NULL)
{
return Info(0, 0);
}
Info left = process(head->left);
Info right = process(head->right);
int maxDistance = max(left.height + 1 + right.height, max(left.maxDistance, right.maxDistance));
int height = max(left.height, right.height) + 1;
return Info(maxDistance, height);
}
题目2:派对的最大快乐值
员工信息的定义如下:
class Employee {
public int happy; //这名员工可以带来的快乐值
List<Employee> subordinates; //这名员工有哪些直接下级
}
公司的每个员工都符合Employee类的描述。整个公司的人员结构可以看作是一棵标准的、没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。
这个公司现在要办party,你可以决定哪些员工来,哪些员工不来。但是要遵循如下规则。
- 如果某个员工来了,那么这个员工的所有直接下级都不能来
- 派对的整体快乐值是所有到场员工快乐值的累加
- 你的目标是让派对的整体快乐值尽量大
给定一棵多叉树的头节点boss,请返回派对的最大快乐值。
解题步骤
- 可能性分析。本题的标准是最大快乐值,同样可以用头节点参与或不参与讨论。
(1)头节点参与。当头节点参与时,它的子节点都不能参与,所以返回每个子节点不参与时的最大快乐值,依次相加并加上当前头节点的快乐值
(2)头节点不参与。它的子节点可以选择参与也可以选择不参与,返回每个子结点参与和不参与两者的较大的值,并相加 - 所需要的信息。子节点参与时的最大快乐值、不参与时的最大快乐值
- 整合后的信息。参与与不参与的最大快乐值
- C++代码设计实现
class Employee
{
public:
int happy; //这名员工可以带来的快乐值
list<Employee*> subordinates; //这名员工有哪些直接下级
};
class Info
{
public:
Info(int particMaxHappiness, int notParticMaxHappiness)
{
this->particMaxHappiness = particMaxHappiness;
this->notParticMaxHappiness = notParticMaxHappiness;
}
public:
int particMaxHappiness;
int notParticMaxHappiness;
};
Info process(Employee* head);
int maxHappy(Employee* head)
{
Info temp = process(head);
return max(temp.particMaxHappiness, temp.notParticMaxHappiness);
}
Info process(Employee* head)
{
if (head->subordinates.size() == 0)
{
return Info(head->happy, 0); //base case是到了叶节点,他参与的最大快乐值就是它本身,不参与就是0
}
int particMaxHappiness = head->happy;
int notParticMaxHappiness = 0;
for (auto node : head->subordinates)
{
Info temp = process(node);
particMaxHappiness += temp.notParticMaxHappiness;
notParticMaxHappiness += max(temp.notParticMaxHappiness, temp.particMaxHappiness);
}
return Info(particMaxHappiness, notParticMaxHappiness);
}
递归套路和Morris遍历的选择
- 当解题方法必须做第三次信息的强整合,那么递归套路是最优解。这里的第三次是指递归遍历时第三次返回本函数(与后序遍历的打印时机一样),而Morris遍历没办法做到第三次。
- 不需要则Morris遍历是最优解
浙公网安备 33010602011771号