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

818 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

派对的最大快乐值

员工信息的定义如下:

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!");
	}
}