求公司派对的最大快乐值
提示:本节仍然是重点说二叉树的DP递归套路,非常重要而且容易理解
二叉树的动态规划树形DP递归套路系列文章有这些,可以帮助你快速掌握树形DP的题目解题思想,就一个套路:
(1)判断二叉树是否为平衡二叉树?树形DP,树形动态规划的递归套路
(2)求二叉树中,任意两个节点之间的距离最大值是多少
(3)求二叉树中,包含的最大二叉搜索子树的节点数量是多少
题目
公司的员工类是:
public static class Employee{
public int happy;
public ArrayList<Employee> nexts;//直接下属
public Employee(int h){
happy = h;
nexts = new ArrayList<>();//空动态数组?就是列表,没问题
}
}
内部有happy值,可能一个员工还有一堆下属:nexts中,那就是多叉树
树的头节点就是董事长
树的叶节点就是没有员工的节点。
每个员工只有一个上级,不会重复的
现在公司要办party,请你发请柬邀请员工,你可以决定哪个员工来还是不来,但是:
(1)一个员工它来了,它的直接下属不能来;
(2)派对的快乐值是所有到场员工快乐值的总和;
(3)你的目标是使得整体派对值最大化;
(4)给你董事长头节点,请你返回最大派对值;
一、审题
示例:
如果A员工来,那A的下属决不能来
如果A不来,那他的下属可以来,也可以不来
二、树形动态规划的递归套路:树形DP
95%的二叉树动态规划问题,都可以用以下套路解决:
一、定义个信息类:Info
收集x左树和右树都有的信息(左右树都有哦,而不是针对某单独的左树或者右树),比如是否平衡?树的高度,总之就是有的信息,不管你是String还是Boolean还是Integer类型的信息。经常是要讨论与x有关,还是 与x无关。
二、树形DP递归套路: 来到x节点
1)base case:考虑叶节点应该返回的信息Info
2)先收集左树和右树的信息Info
3)综合2)整理x自己的信息,并返回;
三、从题目给的二叉树head开始求(2),得到了一个head的信息Info,找里面咱们要用的那个信息:比如是否平衡?返回它。
来,咱们举例说明:实践知真理!
三、解题思路:
本题仍然是树形DP,虽然不是二叉树,但是多叉树动态规划也叫树形DP
一、定义个信息类:Info
收集x左树和右树都有的信息(左右树都有哦,而不是针对某单独的左树或者右树),比如是否平衡?树的高度,总之就是有的信息,不管你是String还是Boolean还是Integer类型的信息。经常是要讨论与x有关,还是 与x无关。
咱们讨论一个员工x来还是不来?
来就与x有关,不来就是和员工x无关
如果员工来【至少x自己的happy值要算上,当然它的多个直接下属不能来】,x极其下面整个树的派对最大值是多少?yes
不过员工不来【x自己提供0快乐值,但是它的下属可以来,可以不来】,x极其下面整个树的派对最大值是多少?no
yes和no就是咱们要收集的信息
统计x极其下面的多叉树,整体最大的快乐值应该是多少?
public static class Info{
public int yes;//来或者不来的派对值
public int no;
public Info(int y, int n){
yes = y;
no = n;
}
}
二、树形DP递归套路: 来到x节点
那就设定一个函数来统计把,process(Node x)
1)base case:考虑叶节点应该返回的信息Info
没有员工的节点x,就是x.nexts=null,那可以统计自己来,还是不来的最大快乐值
自己来就是x.happy,不来,就会0快乐。
返回即可
//终止条件:
if (head.nexts == null) return new Info(head.happy, 0);//自己来不来
2)先收集左树和右树的信息Info
这好说:
对于多叉树,就是收集下属所有员工的信息:
for(Employee next:head.nexts){
Info nextInfo = process(next);//先看看员工来不来
3)综合2)整理x自己的信息,并返回;
整合信息,在每一个2)循环中,咱,需要讨论,
——如果x来,x提供x.happy,那所有next员工不能来,这样的派对最大值是多少?yes
——如果x不能来,x提供了0快乐值,但是你可以决定next来,还是不来,他们能提供的最大值是多少?no
这样就可以算出yes和no信息
因此需要把for循环中的结果累加到yes和no上。
for(Employee next:head.nexts){
Info nextInfo = process(next);//先看看员工来不来
//对其所有下属
yes += nextInfo.no;//自己来了,员工决不能来
no += Math.max(nextInfo.yes, nextInfo.no);//自己不来的话,得看员工来不来的最大值
}
手撕代码整体实现:
//复习
public static Info f(Employee x){
if (x.nexts == null) return new Info(x.happy, 0);
//所有x的员工们的情况:
int yes = x.happy;//我x来
int no = 0;//我x不来,x之下最大快乐值
for(Employee e: x.nexts){
Info eHappy = f(e);
//我来,下属不能来
yes += eHappy.no;
no += Math.max(eHappy.yes, eHappy.no);//我不来,员工随意,他们的最大值加我
}
return new Info(yes, no);//返回x的信息
}
三、从题目给的二叉树head开始求(2),得到了一个head的信息Info,找里面咱们要用的那个信息:比如是否平衡?返回它。
调用,自然是求董事长的Info
然后对比Info的yes和no,谁更大呢?就看董事长来还是不来?
//调用
public static int partyMaxHappy(Employee head){
if (head == null) return 0;
Info info = f(head);
return Math.max(info.yes, info.no);
}
测试一下:
//公司架构怎么样:
// boss:100
// m1:1000 m2:1000 m3:1000
// w1:0 w2:78 w3:58 w4:44 w5:100 w6:22
public static Employee generateEmployee(){
Employee boss = new Employee(100);
Employee m1 = new Employee(1000);
Employee m2 = new Employee(1000);
Employee m3 = new Employee(1000);
Employee w1 = new Employee(0);
Employee w2 = new Employee(78);
Employee w3 = new Employee(58);
Employee w4 = new Employee(44);
Employee w5 = new Employee(100);
Employee w6 = new Employee(22);
boss.nexts.add(m1);
boss.nexts.add(m2);
boss.nexts.add(m3);
m1.nexts.add(w1);
m1.nexts.add(w2);
m2.nexts.add(w3);
m2.nexts.add(w4);
m3.nexts.add(w5);
m3.nexts.add(w6);
return boss;
}
public static void test(){
Employee boss = generateEmployee();
int max = getMaxHappy(boss);
System.out.println(max);
int max2 = partyMaxHappy(boss);
System.out.println(max2);
}
public static void main(String[] arges){
//简称psvm,骚得很
test();
}
结果如下
3000
3000
总结
提示:重要经验:
1)不管是二叉树,还是多叉树,树形DP都是同一个套路,就是去各个子树上收集信息,然后整理x节点自己的信息,往上传;
2)树形DP的递归套路要熟悉,不断使用,突破,然后融会贯通;
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。