力扣 621. 任务调度器

题目描述

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。

然而,两个相同种类的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

你需要计算完成所有任务所需要的最短时间

示例 1

输入:tasks = ["A","A","A","B","B","B"], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
     在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。

示例 2

输入:tasks = ["A","A","A","B","B","B"], n = 0
输出:6
解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
诸如此类

示例 3

输入:tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
输出:16
解释:一种可能的解决方案是:
     A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A

提示

  • 1 <= task.length <= 104
  • tasks[i] 是大写英文字母
  • n 的取值范围为 [0, 100]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/task-scheduler
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


桶思想图解

现在假设:

  • 冷却时间 n = 2
  • 任务列表 tasks = [A, A, A, B, B, B, C]

构造一个宽度为 n + 1 的桶,可以往其中放置 3 个任务。由题意知,n + 1 的桶宽度恰好满足这样的条件:

  • 只要同一个桶不出现相同的任务,就能够满足相同任务间隔至少为 n 的冷却条件。

我们恰好需要 h 个这样的桶,htasks 任务列表中的最大任务数,也就是 3。因为 AB 都出现了 3 次,如果桶个数小于 3,会导致同一个桶中出现相同任务,不满足冷却条件;当然也不能大于 3,因为我们要求的是最小任务耗时。

从上到下把任务放置到桶的空位中,得到下图:

图片

最后一个桶没有填满,所以此时完成任务的总时间为:

  • (h - 1) * (n + 1) + w
  • w 为最后一个桶的实际占用宽度,可以通过计算一共有几种任务达到了最大任务数 h 得出。本例中,AB 都达到了最大任务数,所以 w == 2
  • 代入计算,最后答案为 8

执行任务时按照从左往右,从上往下的顺序,得出来执行任务序列为:

  • [A, B, C, A, B, 冷却, A, B]

现在考虑任务非常多的情况:

  • 冷却时间还是为 n = 2
  • 任务列表增加了一些任务 tasks = [A, A, A, B, B, B, C, C, D, D, E, F, G]

在之前的基础上,只要再填入任务 CD,桶就满了,剩下的往哪里放?

图片

此时可以依次为三个桶扩容,每次扩容一格,如果不够继续追加,填入剩下的任务:

图片

这下所有任务都安排上了!由于剩下的相同任务数必定小于桶的总高度 h,所以在同一个桶中(也就是同一行中)不会出现相同的任务,这样也就保证了冷却时间的条件。

对于这种任务排布,完成任务的总时间恰好等于任务数:

  • len(tasks)

执行任务时,仍然按照从左往右,从上往下的顺序,得出来执行任务序列为:

  • [A, B, C, D, G, A, B, C, E, A, B, D, F]

最终的答案需要在两种情况之间取最大值:

  • max(len(tasks), (h - 1) * (n + 1) + w)

一行解:桶思想

借助海象运算符写出来的一行解。

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        return max(len(tasks), ((h := (c := Counter(tasks)).most_common(1)[0][1]) - 1) * (n + 1) + [*c.values()].count(h))

运行结果

执行结果:通过
执行用时:56 ms, 在所有 Python3 提交中击败了95.04% 的用户
内存消耗:13.8 MB, 在所有 Python3 提交中击败了7.04% 的用户

不过还是来看看四行的正常写法吧:

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        c = Counter(tasks)
        h = c.most_common(1)[0][1]
        w = [*c.values()].count(h)
        return max(len(tasks), (h - 1) * (n + 1) + w)

简单说明:

  • 使用 collections.Countertasks 进行计数,键为大写字母,值为出现频次。
  • h 是桶的个数,为 tasks 任务列表中的最大任务数。
  • w 是最后一个桶的实际占用宽度,为最后一行任务个数,通过计算一共有几种任务达到了最大任务数得出。

2020.12.05


公众号

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页