Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks. Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle.
However, there is a non-negative cooling interval n that means between two same tasks, there must be at least n intervals that CPU are doing different tasks or just be idle.
You need to return the least number of intervals the CPU will take to finish all the given tasks.
Example:
Input: tasks = ["A","A","A","B","B","B"], n = 2 Output: 8 Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.
Constraints:
- The number of tasks is in the range
[1, 10000]
. - The integer
n
is in the range[0, 100]
.
------------------------------------------
这题反应太慢,刚开始还想着DP,发现状态转移方程太复杂。。。然后又想了很久,才想到CPU流水线,想到按照出现频率最高的贪心。。。最后才想到用优先队列每次扒层皮。。可能一次扒多层或者1层,还写了个bug:
import heapq
class Solution:
def leastInterval(self, tasks, n: int) -> int:
td,cycle,last,res = {},n+1,0,0
for task in tasks:
td[task] = td.get(task,0)+1
tv = [-vv for kk,vv in td.items()]
heapq.heapify(tv)
while (len(tv) >= cycle):
topK = []
for i in range(cycle):
topK.append(-heapq.heappop(tv))
delta = topK[-1] if len(tv) >= cycle*2 else 1 #bug1: always topK[-1]
res += delta*cycle
for item in topK:
if (item > delta):
heapq.heappush(tv,-(item-delta))
for j in range(len(tv)):
if (tv[j] == tv[0]):
last += 1
if (tv):
res += (-tv[0]-1)*cycle+last
return res
这题更漂亮的解法是用桶的思想,如下图:
如果n=4,那么流水线的cycle=5。那么整个流水线至少要(最长的任务个数-1)*cycle的时间,也就是红框部分,这都好理解
下面开始脑筋急转弯,从流水线空闲的时钟来分析一共所需的时间。
- 当任务个数<=cycle时,可能存在流水线空闲的时候,也就是图中的idle,算出idle就可以算整个时间
- 当任务个数>cycle时,因为首先会按照每个任务出现频率排序,假设存在idle状态,那么超过cycle的任务一定可以塞到idle状态里。
- 当超过cycle的任务持续增多,此时是需要脑筋急转弯的时候,假设所有idle状态都满了。如果把流水线每一个cycle看成一个桶,那么这些桶里之前装的都是互不相同的元素。当新任务来的时候,由于之前预先排序的设定,新任务的个数x一定小等于已有桶的个数,那么从已有x个桶中可以抽出x个互不相同的元素,放到新桶中,旧桶空出来的位置放新来的x个元素。也就是说从此流水线不存在idle的可能。。。
以下是codes:
class Solution:
def leastInterval(self, tasks, n: int) -> int:
td, cycle, idle, moretasks, tl = {}, n + 1, 0, 0, len(tasks)
for task in tasks:
td[task] = td.get(task, 0) + 1
tv = [vv for kk, vv in td.items()]
tv.sort(reverse=True)
tvl = len(tv)
if (tvl <= 0):
return 0
height = tv[0]-1
for i in range(1, cycle):
idle += max(0,height-tv[i]) if i < tvl else height #bug2: forget idle space
for i in range(cycle, tvl):
moretasks += tv[i]
return tl if moretasks >= idle else tl+idle-moretasks #bug1: >= instead of >
s = Solution()
print(s.leastInterval(["A","A","A","A","A","A","B","C","D","E","F","G"],2))