Description
给你一个用字符数组\(tasks\)表示的CPU需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在\(1\)个单位时间内执行完。在任何一个单位时间,CPU可以完成一个任务,或者处于待命状态。
然而,两个相同种类的任务之间必须有长度为整数\(n\)的冷却时间,因此至少有连续\(n\)个单位时间内CPU在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的最短时间。
Constraints
- \(1 \le task.length \le 10^4\)
- \(0 \le n \le 100\)
Solution
一个基本的思路就是字符轮流贪心取,没办法再待命
具体到数据结构上,用2个堆来维护,一个大根堆维护\(\{cnt, char\}\),另一个小根堆维护\(\{d, cnt, char\}\)。其中\(char\)表示字符,\(cnt\)表示剩余次数,\(d\)表示冷却到达时间。每次从大根堆消费一个字符后就算好对应的\(d\)并放入小根堆\(\{d, cnt-1, char\}\),下一次消费再尝试捞出来
复杂度\(O(nlogn)\),这里\(n\)表示\(task\)的长度
Code
class Solution {
public:
int leastInterval(vector<char>& tasks, int n) {
using Tup2 = std::tuple<int, char>;
using Tup3 = std::tuple<int, int, char>;
auto pcnt = priority_queue<Tup2> {};
auto pduration
= priority_queue<Tup3, vector<Tup3>, greater<Tup3>> {};
int cnt[128] {};
for(auto t : tasks) cnt[t]++;
for(int i = 0; i < 128; ++i) {
// NOTE
if(!cnt[i]) continue;
pcnt.emplace(cnt[i], i);
}
int consumed = 0;
int clock = 0;
while(consumed < tasks.size()) {
while(!pduration.empty()) {
auto [d, c, a] = pduration.top();
if(clock < d) break;
pduration.pop();
pcnt.emplace(c, a);
}
if(!pcnt.empty()) {
auto [c, a] = pcnt.top();
pcnt.pop();
consumed++;
if(--c > 0) {
pduration.emplace(clock + n + 1, c, a);
}
}
if(pcnt.empty() && !pduration.empty()) {
clock = get<0>(pduration.top());
} else {
clock++;
}
}
return clock;
}
};