题目描述
给你一个用字符数组 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
个这样的桶,h
为 tasks
任务列表中的最大任务数,也就是 3
。因为 A
和 B
都出现了 3
次,如果桶个数小于 3
,会导致同一个桶中出现相同任务,不满足冷却条件;当然也不能大于 3
,因为我们要求的是最小任务耗时。
从上到下把任务放置到桶的空位中,得到下图:
最后一个桶没有填满,所以此时完成任务的总时间为:
(h - 1) * (n + 1) + w
w
为最后一个桶的实际占用宽度,可以通过计算一共有几种任务达到了最大任务数h
得出。本例中,A
和B
都达到了最大任务数,所以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]
在之前的基础上,只要再填入任务 C
和 D
,桶就满了,剩下的往哪里放?
此时可以依次为三个桶扩容,每次扩容一格,如果不够继续追加,填入剩下的任务:
这下所有任务都安排上了!由于剩下的相同任务数必定小于桶的总高度 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.Counter
对tasks
进行计数,键为大写字母,值为出现频次。 h
是桶的个数,为tasks
任务列表中的最大任务数。w
是最后一个桶的实际占用宽度,为最后一行任务个数,通过计算一共有几种任务达到了最大任务数得出。
2020.12.05