题目
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 intervaln that means between twosame 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 1:
Input: tasks = ['A','A','A','B','B','B'], n = 2
Output: 8
Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.
Note:
- The number of tasks is in the range [1, 10000].
- The integer n is in the range [0, 100].
题意
关键:模拟CPU任务分配,A 到 Z表示不同的任务,任务可以以不同顺序进行。每个任务可以在一个时间间隔中完成。对于一个时间间隔,CPU可以执行一个任务或者是闲置。但是,两个同样的任务之间需要有 n 个冷却时间,也就是说假如A执行后,那么未来n个时间间隔内,A是不允许再执行的。
说明:
该问题可以看做区间找点问题的扩展,只不过可让区间进行扩展。
分析及解答
解法1:【贪心 + 数学表达】
- 【贪心算法】角度的选择很重要。作者在这里采取了分块的形式,按照出现频率最多(假设频率为k)的将其分为了k块,然后每一轮向这k个区间个插入1个。如何选择贪心策略?
- 【数学公式表达】通过数学公式明确考虑问题的角度,清晰表达解答问题的思路,明确其中涉及的变量以及变量函数间的关系。
- 【证明贪心有效性】如何证明一个贪心策略是能够解决一个问题的?
来自:concise Java Solution O(N) time O(26) space
// (c[25] - 1) * (n + 1) + 25 - i is frame size
// when inserting chars, the frame might be "burst", then tasks.length takes precedence
// when 25 - i > n, the frame is already full at construction, the following is still valid.
public class Solution {
public int leastInterval(char[] tasks, int n) {
int[] c = new int[26];
for(char t : tasks){
c[t - 'A']++;
}
Arrays.sort(c);
int i = 25;
while(i >= 0 && c[i] == c[25]) i--;
return Math.max(tasks.length, (c[25] - 1) * (n + 1) + 25 - i);
}
}
关于上面说法的说明:
- 【分块】依据出现最多的任务(假如是A)进行分块.
- 【问题规约】按照任务从多到少依次为每个块分配任务。我们这里假定,A一定能够符合距离下一个A的冷却时间为n,那么跟在A后面的也一定符合。只要保证A符合就行了,其他任务的的符合要求都可以由A的符合推导出来。
First consider the most frequent characters, we can determine their positions first and use them as a frame to insert the remaining less frequent characters. Here is a proof by construction:
Let F be the set of most frequent chars with frequency k.
Then we can create k chunks, each chunk is identical and is a string consists of chars in F in a specific fixed order.
Let the heads of these chunks to be H_i, then H_2 should be at least n chars away from H_1, and so on so forth; then we insert the less frequent chars into the gaps between these chunks sequentially one by one ordered by frequency in a decreasing order and try to fill the k-1 gaps as full or evenly as possible each time you insert a character.In summary, append the less frequent characters to the end of each chunk of the first k-1 chunks sequentially and round and round, then join the chunks and keep their relative distance from each other to be at least n.
Examples:
AAAABBBEEFFGG 3
here X represents a space gap:
Frame: "AXXXAXXXAXXXA"
insert 'B': "ABXXABXXABXXA" <--- 'B' has higher frequency than the other characters, insert it first.
insert 'E': "ABEXABEXABXXA"
insert 'F': "ABEFABEXABFXA" <--- each time try to fill the k-1 gaps as full or evenly as possible.
insert 'G': "ABEFABEGABFGA"
AACCCBEEE 2
3 identical chunks "CE", "CE CE CE" <-- this is a frame
insert 'A' among the gaps of chunks since it has higher frequency than 'B' ---> "CEACEACE"
insert 'B' ---> "CEABCEACE" <----- result is tasks.length;
AACCCDDEEE 3
3 identical chunks "CE", "CE CE CE" <--- this is a frame.
Begin to insert 'A'->"CEA CEA CE"
Begin to insert 'B'->"CEABCEABCE" <---- result is tasks.length;
ACCCEEE 2
3 identical chunks "CE", "CE CE CE" <-- this is a frame
Begin to insert 'A' --> "CEACE CE" <-- result is (c[25] - 1) * (n + 1) + 25 -i = 2 * 3 + 2 = 8