1. 程式人生 > >leetcode -- 621. Task Scheduler【貪婪演算法 + 數學公式化 + 邏輯證明方式】

leetcode -- 621. Task Scheduler【貪婪演算法 + 數學公式化 + 邏輯證明方式】

題目

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:

  1. The number of tasks is in the range [1, 10000].
  2. The integer n is in the range [0, 100].

題意

關鍵:模擬CPU任務分配,A 到 Z表示不同的任務,任務可以以不同順序進行。每個任務可以在一個時間間隔中完成。對於一個時間間隔,CPU可以執行一個任務或者是閒置。但是,兩個同樣的任務之間需要有 n 個冷卻時間,也就是說假如A執行後,那麼未來n個時間間隔內,A是不允許再執行的。

說明

該問題可以看做區間找點問題的擴充套件

,只不過可讓區間進行擴充套件

分析及解答


解法1:【貪心 + 數學表達】

  • 貪心演算法】角度的選擇很重要。作者在這裡採取了分塊的形式,按照出現頻率最多(假設頻率為k)的將其分為了k塊,然後每一輪向這k個區間個插入1個。如何選擇貪心策略
  • 數學公式表達】通過數學公式明確考慮問題的角度,清晰表達解答問題的思路,明確其中涉及的變數以及變數函式間的關係。
  • 證明貪心有效性】如何證明一個貪心策略是能夠解決一個問題的?
// (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