JAVA程序设计:LCP 10. 二叉树任务调度(2020 力扣杯!Code Your Future 春季全国编程大赛 )

任务调度优化是计算机性能优化的关键任务之一。在任务众多时,不同的调度策略可能会得到不同的总体执行时间,因此寻求一个最优的调度方案是非常有必要的。

通常任务之间是存在依赖关系的,即对于某个任务,你需要先完成他的前导任务(如果非空),才能开始执行该任务。我们保证任务的依赖关系是一棵二叉树,其中 root 为根任务,root.left 和 root.right 为他的两个前导任务(可能为空),root.val 为其自身的执行时间。

在一个 CPU 核执行某个任务时,我们可以在任何时刻暂停当前任务的执行,并保留当前执行进度。在下次继续执行该任务时,会从之前停留的进度开始继续执行。暂停的时间可以不是整数。

现在,系统有两个 CPU 核,即我们可以同时执行两个任务,但是同一个任务不能同时在两个核上执行。给定这颗任务树,请求出所有任务执行完毕的最小时间。

示例 1:

image.png

输入:root = [47, 74, 31]

输出:121

解释:根节点的左右节点可以并行执行31分钟,剩下的43+47分钟只能串行执行,因此总体执行时间是121分钟。

示例 2:

image.png

输入:root = [15, 21, null, 24, null, 27, 26]

输出:87

示例 3:

image.png

输入:root = [1,3,2,null,null,4,4]

输出:7.5

限制:

1 <= 节点数量 <= 1000
1 <= 单节点执行时间 <= 1000

吐槽:比赛时以为是水题,上去敲了个假算法,直到看到了样例3。。。

这次样例真的很良心哇,样例3直接把我劝退了,比赛时脑子抽了,算了半天才知道样例三中7.5是如何得到的,首先让节点值为3的节点跑1秒,同时,两个节点值为4的节点分别跑0.5秒,此时总花费为1秒,之后同时跑剩下的两个3.5,再然后之后串行执行任务了,于是总花费为1+3.5+2+1=7.5。

思路:赛后下来敲了个复杂的树形dp,但是仍然出现了一些小问题(但是看很多人是树形dp做的,可能是我姿势问题吧),去看了榜单发现一个大佬用了7行的dfs过了这道题(想想我接近100行的树形dp,是真的菜哇)。因此这里我用大神的做法补了这道题,说实话我也理解了很久他的思路。

具体思路是这样的(我的理解可能有瑕疵,还望指正):对于每个节点,我们需要完成它的所有前置任务(也就是该节点左右子树)才能执行,因为我们有两个CPU可以并行执行任务,因此能完成前置任务的最优时间就是其单核完成所有前置任务的总时间除以2但是我们可能会遇到这样一种情况(例如样例1)因为我们知道两个CPU是无法同时完成一项任务的,当其中一项任务比另一项任务所花费的时间长的多时,并且只剩下这两个任务需要执行才能执行接下来的任务,此时其中一个CPU就需要等另一个CPU执行完才能往下执行了。这时能完成当前节点所有前置任务的局部最优解就一定是其两个子树分别拥有两个CPU并行执行任务所花费时间的最大值。而对于左右子树可以同时执行完的情况,即我们之前提到的最优解(说白了就是看其中一个子树执行完能不能多出来时间匀给另一颗子树)。因此全局最优解就一定是这两种情况下所花费时间的最大值。

class Solution {
    public double minimalExecTime(TreeNode root) {
    	
    	if(root==null) return 0;
    	
    	double left=minimalExecTime(root.left);
    	double right=minimalExecTime(root.right);
    	
    	return Math.max(left, Math.max(right, (getSum(root.left)+getSum(root.right))/2))+root.val;
    }
    
    private double getSum(TreeNode root) {
    	
    	if(root==null) return 0;
    	
    	return root.val+getSum(root.left)+getSum(root.right);
    	
    }
}

 

你可能感兴趣的:(JAVA程序设计:LCP 10. 二叉树任务调度(2020 力扣杯!Code Your Future 春季全国编程大赛 ))