二叉树的递归套路——派对的最大快乐值

简介: 二叉树的递归套路——派对的最大快乐值


派对的最大快乐值

员工信息的定义如下:

public static class Employee{
  public int happy;//这名员工可以带来的快乐值
  List<Employee> subordinates;//这名员工有哪些直接下级
}

公司的每个员工都符合Employee类的描述。整个公司的人员结构可以看作是一棵标准的、没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。

这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:

  1. 如果某个员工来了,那么这个员工的所有直接下级都不能来
  2. 派对的整体快乐值是所有到场员工快乐值的累加
  3. 你的目标是让派对的整体快乐值尽量大

给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

第一种可能性:X结点来(就是给X结点发请柬),假设X有三个直接下级a,b,c,如果给X发请柬的话,快乐值就是:

X自己的快乐值
+
a不来情况下整颗子树的快乐值
+
b不来情况下整颗子树的快乐值
+
c不来情况下整颗子树的快乐值

因为a,b,c不能来。

第二种可能性:X结点不来(就是不给X发请柬),假设X有三个直接下级a,b,c,如果不给X发请柬的话,快乐值就是:

X自己的快乐值0
+
max{a来的情况下整颗树的快乐值,a不来的情况下整棵树的快乐值}
+
max{b来的情况下整颗树的快乐值,b不来的情况下整棵树的快乐值}
+
max{c来的情况下整颗树的快乐值,c不来的情况下整棵树的快乐值}

所以任何子树都需要返回的信息就是:

  • 头结点来的情况下,整颗子树的快乐值是多少
  • 头结点不来的情况下,整颗子树的快乐值是多少
public static class Info{
    public int yes;// 对于任何子树都要返回 头结点来的情况下,整颗子树的快乐值
    public int no;// 对于任何子树都要返回 头结点不来的情况下,整颗子树的快乐值
    public Info(int y,int n) {
      yes=y;
      no=n;
    }
  }

完整代码:

package com.harrison.class08;
import java.util.ArrayList;
import java.util.List;
public class Code04_MaxHappy {
  public static class Employee{
    public int happy;//这名员工可以带来的快乐值
    List<Employee> nexts;//这名员工有哪些直接下级
    public Employee(int h) {
      happy = h;
      nexts = new ArrayList<>();
    }
  }
  public static class Info{
    public int yes;// 对于任何子树都要返回 头结点来的情况下,整颗子树的快乐值
    public int no;// 对于任何子树都要返回 头结点不来的情况下,整颗子树的快乐值
    public Info(int y,int n) {
      yes=y;
      no=n;
    }
  }
  public static Info process1(Employee x) {
    if(x==null) {
      return new Info(0, 0);
    }
    // X来的情况下先直接获得X的快乐值
    int yes=x.happy;
    // X不来情况下,快乐值就是0
    int no=0;
    // X来:累加上X的直接下级不来的情况下的快乐值
    // X不来:累加上X的直接下级来的情况和不来的情况 的 最大值
    for(Employee next:x.nexts) {
      Info nextInfo=process1(next);
      yes+=nextInfo.no;
      no+=Math.max(nextInfo.yes, nextInfo.no);
    }
    return new Info(yes, no);
  }
  public static int maxHappy1(Employee head) {
    Info allInfo=process1(head);
    return Math.max(allInfo.yes, allInfo.no);
  }
  // 当前来到的节点叫cur,
  // up表示cur的上级是否来,
  // 该函数含义:
  // 如果up为true,表示在cur上级已经确定来,的情况下,cur整棵树能够提供最大的快乐值是多少?
  // 如果up为false,表示在cur上级已经确定不来,的情况下,cur整棵树能够提供最大的快乐值是多少?
  public static int process2(Employee cur, boolean up) {
    if (up) { // 如果cur的上级来的话,cur没得选,只能不来
      int ans = 0;
      for (Employee next : cur.nexts) {
        ans += process2(next, false);
      }
      return ans;
    } else { // 如果cur的上级不来的话,cur可以选,可以来也可以不来
      int p1 = cur.happy;
      int p2 = 0;
      for (Employee next : cur.nexts) {
        p1 += process2(next, true);
        p2 += process2(next, false);
      }
      return Math.max(p1, p2);
    }
  }
  public static int maxHappy2(Employee boss) {
    if (boss == null) {
      return 0;
    }
    return process2(boss, false);
  }
  public static void generateNexts(Employee e,int level,int maxLevel,int maxNexts,int maxHappy) {
    if(level>maxLevel) {
      return ;
    }
    int nextSize=(int)(Math.random()*(maxNexts+1));
    for(int i=0; i<nextSize; i++) {
      Employee next=new Employee((int)(Math.random()*(maxHappy+1)));
      e.nexts.add(next);
      generateNexts(next, level+1, maxLevel, maxNexts, maxHappy);
    }
  }
  public static Employee generateBoss(int maxLevel,int maxNexts,int maxHappy) {
    if(Math.random()<0.02) {
      return null;
    }
    Employee boss=new Employee((int)(Math.random()*(maxHappy+1)));
    generateNexts(boss, 1, maxLevel, maxNexts, maxHappy);
    return boss;
  }
  public static void main(String[] args) {
    int maxLevel = 4;
    int maxNexts = 7;
    int maxHappy = 100;
    int testTimes = 100000;
    for (int i = 0; i < testTimes; i++) {
      Employee boss = generateBoss(maxLevel, maxNexts, maxHappy);
      if (maxHappy1(boss) != maxHappy2(boss)) {
        System.out.println("Oops!");
      }
    }
    System.out.println("finish!");
  }
}

   


相关文章
|
存储 C++ 容器
五道超经典题目,带你手撕链表题(多种方法实现)下
五道超经典题目,带你手撕链表题(多种方法实现)
74 1
|
9月前
|
机器学习/深度学习 算法
六六力扣刷题双指针之三数之和
六六力扣刷题双指针之三数之和
72 0
《蓝桥杯每日一题》递归·AcWing 1497. 树的遍历
《蓝桥杯每日一题》递归·AcWing 1497. 树的遍历
69 0
五道超经典题目,带你手撕链表题(多种方法实现)上
五道超经典题目,带你手撕链表题(多种方法实现)
129 0
|
算法 C++
【快乐手撕LeetCode题解系列】——合并两个有序数组
哈喽各位友友们😊,我今天又学到了很多有趣的知识,现在迫不及待的想和大家分享一下!😘我仅已此文,和大家分享【快乐手撕LeetCode题解系列】——合并两个有序数组~ 都是精华内容,可不要错过哟!!!😍😍😍
78 0
|
算法 C++
【快乐手撕LeetCode题解系列】——删除有序数组中的重复项
哈喽各位友友们😊,我今天又学到了很多有趣的知识,现在迫不及待的想和大家分享一下!😘我仅已此文,和大家分享【快乐手撕LeetCode题解系列】——删除有序数组中的重复项~ 都是精华内容,可不要错过哟!!!😍😍😍
71 0
|
算法 C++
【快乐手撕LeetCode题解系列】——移除链表元素
哈喽各位友友们😊,我今天又学到了很多有趣的知识,现在迫不及待的想和大家分享一下!😘我仅已此文,和大家分享【快乐手撕LeetCode题解系列】——移除链表元素~ 都是精华内容,可不要错过哟!!!😍😍😍
86 0
|
存储 移动开发
【蓝桥杯集训·每日一题】AcWing 1497. 树的遍历
文章目录 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 三、知识风暴 递归
84 0
|
算法 JavaScript 前端开发
日拱算法:双指针解“判断子序列”,除夕快乐~
算法继续,本篇带来的是非常典型的一道题:“判断子序列”,采用的是双指针的解法~
|
存储 算法
学会二叉树不知道干啥?二叉树的深度优先搜索和广度优先搜索,我要打十个乃至二十个(打开你的LeetCode撸起来)学练并举
学会二叉树不知道干啥?二叉树的深度优先搜索和广度优先搜索,我要打十个乃至二十个(打开你的LeetCode撸起来)学练并举
学会二叉树不知道干啥?二叉树的深度优先搜索和广度优先搜索,我要打十个乃至二十个(打开你的LeetCode撸起来)学练并举