题目描述:

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

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

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

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

示例 1:

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

输出:121

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

示例 2:

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

输出:87

示例 3:

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

输出:7.5

限制:

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

分析

这题也是实打实的hard难度问题,一开始看见题目觉得挺简单的,一看用例3出来个7.5就懵了,在看评论了解到7.5是怎么来的后还是没有思路,看了若干题解后还是没有理解解题思路,最后自己手动推导,才把这题给整出来,思维难度妥妥的五颗星。

系统有两个 CPU 核,即我们可以同时执行两个任务,但是同一个任务不能同时在两个核上执行。这句话是本题的核心,如果没有用例三,百分之九十的人都会觉得只要求出左右子树串行执行的时间和并行执行的时间,简单运算后就能推出整棵树的最短执行时间了。有两个CPU核,那么所有任务执行期间要么是两个核同时执行不同的任务,要么是只有一个核在工作,两个核同时工作的时间我们称为并行时间,单个核工作的时间我们称为串行时间。对于以root为根的树,设其左子树任务执行的串行时间是a,并行时间是b;右子树的串行时间是c,并行时间是d。我们第一印象就是把两个子树的串行执行时间利用起来,比如a > c,我们用c的时间并行执行,这样左右子树串行的时间就只剩下a - c了,加上root的串行执行时间,再加上所有的并行时间就是总时间了。比如用例3,先并行执行4 4,耗时4,再尝试并行执行原本需要串行执行的3和2,并行执行2时间,串行执行1时间,最后再串行执行1,这样得到的总时间是4 + 2 + 1 + 1 = 8,大于用例的输出7.5,说明这种方法不是最优的。

那么用例3是怎么执行起来的呢?1的孩子是3,2,2的孩子是4,4。我们之前的思路并行执行3,2时候2执行完3还剩下1时间只能串行执行了,因为两个核不能同时执行同一个的任务。题目里有“我们可以在任何时刻暂停当前任务的执行,并保留当前执行进度”,这样我们能不能先把3的1时间给消耗掉,然后暂停,最后再和2一起并行执行呢?答案是可以的。前0.5时间我们并行执行3和4,之后一个核换另一个4来执行,这样1时间过去了,2的两个孩子的任务时间就变成了3.5, 3的任务时间变成了2.之后3.5时间执行我们并行执行两个3.5的任务,之后的2时间我们并行执行2和3(任务时间只剩下了2),最后串行执行1,总的时间就是1 + 3.5 + 2 + 1 = 7.5。

对用例3的模拟能够帮助我们解决本题,如果只看上面的模拟过程,就会疑惑,为什么要先把3拆两个0.5的时间和4去并行执行,0.5是怎么来的?求解本题时我们要怎么去拆分时间呢?不妨宏观的看待这一过程,1是根节点,首先考察其左子树3的执行最短时间,由于3没有孩子节点,只能串行执行,所以耗时3执行完,其中串行时间(也就是一个核执行时间)是3,并行时间(双核执行时间)是0,用前面的字母表示就是a = 3,b = 0;1的右子树串行执行时间是c = 2,并行执行时间的d = 4。这是显然的,按照我们一开始的思路,首先把两个子树的串行时间并行起来,也就是串行时间a和c并行执行,这样其中的2时间就变成了并行时间,还剩1时间我们尝试用1的右子树的并行执行时间消耗掉。怎么消耗掉?相当于我们两只手不能拿同类东西,现在有三类物品,第一类和第二类各有4kg,第三类物品有1kg,我们如果开始拿1,2类物品,耗时4拿完,之后1时间一只手拿第三类物品;如果先拿一半3和同样多的1,再拿另一半3和同样多的2,这样3物品拿完了,剩下两种物品都只剩下3.5kg了,继续双手拿完,拿物品顺序的改变就使得全程我们双手都没有休息,执行时间减少。

总结下本题的思路,对于以root为根的树,设其左子树任务执行的串行时间是a,并行时间是b;右子树的串行时间是c,并行时间是d。不妨设a > c,为了使得任务执行时间最短,我们将其中c的串行时间用于a和c并行,剩下的a - c的串行时间尝试用并行时间d消耗掉,也就是说用右子树的串行时间和并行时间去消耗掉左子树的串行时间,为什么不用左子树的并行时间消耗呢?因为我们求出的a和b已经是左子树的最小串行和并行时间了,没法再从左子树本身去减少串行时间了。d能够消耗掉多少a -c的串行时间呢?

我们以用例1为例

我们递归的求出了47的左子树的串行时间a = 74,并行时间b = 0;右子树串行时间c = 31,并行时间d = 0.按照上面的理论,先用右子树串行时间31去消耗左子树的串行时间,增加了整棵树的并行时间31.这时候左子树还剩下43的串行时间,但是由于右子树的并行时间是0,没有任务可以和74所在的任务继续并行执行了,所以串行时间不能再减少了。如果给31增加两个15时间的孩子节点,这样右孩子的并行时间d = 15了,我们先用15时间去执行74任务和一个15任务;再用15时间去执行74任务和另一个15任务,这样一来两个并行的15任务执行完之后74的串行时间又减少了30,43 - 30 = 13,d = 15时为74再次减少了30的串行时间;如果右子树的并行时间是d = 21.5呢,那么两个21.5的任务依次去和74任务并行执行,恰好能够消耗掉剩下的43串行时间。如果d继续增加呢,比如加到30,我们依旧只能用21.5的时间轮流和74任务并行,74任务执行完后就没有串行任务了,剩下的右子树的并行任务继续执行。根据这个例子我们可以得出,左子树的串行时间a其中的c时间用右子树的串行时间c消耗掉,剩下的a -c的串行时间可以尝试用右子树的并行时间d消耗掉。

如果a - c <= 2 * d,那么两个执行时间为d的任务可以轮流消耗掉a - c的串行时间;
如果a - c > 2 * d,那么两个执行时间为d的任务只能消耗掉2d的串行时间。

解题思路已经显然了,递归的求出root左右子树的串、并行时间ab和cd,如果c > a就交换a和c、b和d,保证下左子树的串行时间比较大。之后求以root为根的树的串行时间和并行时间,串行时间首先要加上root自身的执行时间root->val,然后加上左、右子树的串行时间,假设d能够消耗掉左子树的串行时间是t,加上c消耗掉的串行时间c,那么左右子树剩下的串行时间就是a - c - t。并行时间则等于原本左右子树的并行时间b + d,加上原本c时间串行转换成的并行时间,再加上用d消耗掉的左子树的串行时间t / 2,为什么t要除以2?回到a - c = 1,d = 4的这个例子,如果两个核能够执行相同的任务,那么串行时间1只需要并行执行0.5就可以完成了。改变执行顺序的结果是等价于并行执行a -c这个任务的,0.5时间执行4和1, 0.5时间执行另一个4和1,消耗1时间,剩下的3.5时间执行完两个4剩下的,这样的并行时间等于4.5,相比于原来的d = 4增加的恰好是0.5.所以串行任务改为并行执行后,增加的并行时间自然等于串行时间 / 2。

最后,整个树的执行时间等于串行时间+并行时间。

代码

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:pair<int,double> dfs(TreeNode* root){if(!root)   return {0,0};auto x = dfs(root->left);auto y = dfs(root->right);if(x.first < y.first)   swap(x,y);int a_c = x.first - y.first;//减去串行并行后的串行时间int t = a_c;//剩下的串行时间能够用于并行的时间if(t > 2 * y.second)    t = 2 * y.second;int a = root->val + a_c - t;double b = x.second + y.second + y.first + t / 2.0;return {a,b};}double minimalExecTime(TreeNode* root) {auto res = dfs(root);return res.first + res.second;}
};

leetcode LCP 10. 二叉树任务调度相关推荐

  1. LeetCode LCP 34. 二叉树染色(树上DP)

    文章目录 1. 题目 2. 解题 1. 题目 小扣有一个根结点为 root 的二叉树模型,初始所有结点均为白色,可以用蓝色染料给模型结点染色,模型的每个结点有一个 val 价值. 小扣出于美观考虑,希 ...

  2. LeetCode实战:二叉树的最近公共祖先

    背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Given a bin ...

  3. LeetCode实战:二叉树中的最大路径和

    背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Given a non ...

  4. LeetCode Python实现 二叉树简单部分

    LeetCode Python实现 二叉树简单部分 ''' 1 二叉树的最大深度 给定一个二叉树,找出其最大深度.二叉树的深度为根节点到最远叶子节点的最长路径上的节点数.说明: 叶子节点是指没有子节点 ...

  5. 【算法】贪心算法:LeetCode 714 买卖股票的最佳时机含手续费 、LeetCode 968 监控二叉树

    LeetCode 714 买卖股票的最佳时机含手续费 (中等) 题目 描述 给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 :整数 fee 代表了交易股票的手续费用. 你 ...

  6. Leetcode.617 合并二叉树

    题目链接 Leetcode.617 合并二叉树 easy 题目描述 给你两棵二叉树: root1和 root2. 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会) ...

  7. ⭐算法入门⭐《二分枚举》简单15 —— LeetCode LCP 18. 早餐组合

    文章目录 一.题目 1.题目描述 2.基础框架 3.原题链接 二.解题报告 1.思路分析 2.时间复杂度 3.代码详解 三.本题小知识 四.加群须知 一.题目 1.题目描述   小扣在秋日市集选择了一 ...

  8. LeetCode——LCP 29. 乐团站位[简单]——分析及代码(Java)

    LeetCode--LCP 29. 乐团站位[简单]--分析及代码[Java] 一.题目 二.分析及代码 1. 直接计算 (1)思路 (2)代码 (3)结果 三.其他 一.题目 某乐团的演出场地可视作 ...

  9. leetcode 617. 合并二叉树 思考分析

    题目 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠. 你需要将他们合并为一个新的二叉树.合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否 ...

最新文章

  1. AngularJs表单自动验证
  2. python 网络聊天服务端
  3. 线程堆栈大小 pthread_attr_setstacksize 的使用
  4. 系统运维包括哪些内容_智能养老系统包括哪些?养老管理系统内容详解
  5. php和python区别-python与php比较
  6. Hadoop详解(七):YARYN完全分布式环境搭建
  7. 【学习笔记】ABAP OOD设计模式 - 观察者模式
  8. windows(64位)下使用curl命令
  9. uva 1611——Crane
  10. QT5开发及实例学习之十五Qt5位置相关函数
  11. ShuffleNetV2论文译读笔记
  12. 拓端tecdat|WinBUGS对多元随机波动率模型:贝叶斯估计与模型比较
  13. matlab把数据乘,【excel怎么相乘】如何把EXCLE数据导入到MATLAB中
  14. ckeditor java 取值_Jquery 对Ckeditor 取值
  15. s5p4418的uboot网络无法使用问题解决
  16. 为什么有些“业余”的能赢。
  17. 造命者天,立命者我_huadingjin_新浪博客
  18. 怎么理解产品经理职位?
  19. STM32低功耗总结——转载
  20. 外贸人如何从SiteGround购买建站外贸主机

热门文章

  1. python - udp socket通信循环发送和接收数据
  2. 海信IP906H-高安版-HI3798MV100-EMMC-当贝桌面强刷固件包
  3. Modeling生涯中的苦与乐
  4. Tableau制作环形图
  5. Python开发工具系列1------基于业务常见情况,利用Python实现短文本相似度模型并且利用PyQt5封装为工具
  6. 多线程-静态代理-Lambda表达式
  7. 2021星空实施在线认证初级班(1)
  8. 数码类测评:dido G28S Pro心电血压智能手表
  9. 【转】ASPLOS'17论文导读——SC-DCNN: Highly-Scalable Deep Convolutional Neural Network using Stochastic Comput
  10. 天野学院第二期模拟班