LeetCode 0621 -- 任務調度器

任務調度器

題目描述

給定一個用字符數組表示的 CPU 需要執行的任務列表。其中包含使用大寫的 A - Z 字母表示的26 種不同種類的任務。任務可以以任意順序執行,並且每個任務都可以在 1 個單位時間內執行完。CPU 在任何一個單位時間內都可以執行一個任務,或者在待命狀態。

然而,兩個相同種類的任務之間必須有長度爲 n 的冷卻時間,因此至少有連續 n 個單位時間內 CPU 在執行不同的任務,或者在待命狀態。

你需要計算完成所有任務所需要的最短時間。

示例 1:

輸入: tasks = ["A","A","A","B","B","B"], n = 2
輸出: 8
執行順序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B.

注:

  1. 任務的總個數爲 [1, 10000]。
  2. n 的取值範圍爲 [0, 100]。

解題思路

個人AC

class Solution {
    public int leastInterval(char[] tasks, int n) {
        int st = 0;
        int[] cnts = new int[26];
        int total = 0;
        for (int i = 0; i < tasks.length; i++) {
            cnts[tasks[i] - 'A']++;
            total++;
        }
        // 記錄任務'X'最後一次執行的時間,從1開始
        // key的類型爲Integer,A對應0,B對應1,依此類推...
        // value的類型爲Integer,對應其最後一次出現的時間
        HashMap<Integer, Integer> map = new HashMap<>();
        while (total > 0) {
            for (int i = 0; total > 0 && i < cnts.length; i++) {
                if (cnts[i] == 0) continue;

                cnts[i]--;
                total--;
                
                if (map.containsKey(i)) {
                    int interval = st - map.get(i);
                    if (n - interval > 0) st += n - interval;
                }
                st += 1;
                map.put(i, st);
                System.out.println(st);
            }
        }
        return st;
    }
}

未通過。

應該在相同任務等待間隔中儘可能地安排其他任務。

最優解

排序


由於相同的任務之間必須有 n 的冷卻時間,所以我們可以想到按照任務的數量來安排它們,即一種任務的出現次數越多,我們就越早地安排。例如有 5 種任務 A, B, C, D, E,且它們分別有 6, 1, 1, 1, 1 個時,假設冷卻時間 n = 2,那麼我們首先安排任務 A,隨後在 2 單位的冷卻時間裏,我們安排任務 B, C,隨後繼續安排任務 A,再安排任務 D, E,以此類推。

因此我們得到了一種安排的方法:我們規定 n + 1 個任務爲一輪,這樣的好處是同一輪中一個任務最多隻能被安排一次。在每一輪中,我們將當前的任務按照它們剩餘的次數降序排序,並選擇剩餘次數最多的 n + 1 個任務依次執行。如果任務的種類 t 少於 n + 1 個,就只選擇全部的 t 種任務,其餘的時間空閒。這樣做的正確性在於,由於冷卻時間的存在,出現次數較多的那些任務如果不盡早安排,將會導致大量空閒時間的出現,因此貪心地將出現次數較多的任務安排在前面是合理的。同時我們可以保證,這一輪的第 k 個任務距離上一次執行至少有 n 個單位的冷卻時間。我們可以使用逆向思維來證明:假設第 r 輪中某個任務在第 k 個執行,那麼說明它在第 r 輪時爲數量第 k 多的任務。在第 r 輪結束後,第 1 多到第 k 多的任務的數量都會減少 1,因此在第 r + 1 輪,這個任務最多也只能是數量第 k 多,因此它如果被執行,一定滿足冷卻時間的要求。

根據上面的安排方法,我們每一輪選擇不超過 n + 1 個任務執行,直到所有的任務被執行。


  1. 將任務按類型進行分組,統計個數;
  2. 對得到的分組進行排序;
  3. 首先排列個數最多的任務T,需要的時間最多爲 T -> X * n -> T -> X * n -> T:
    • st = (m - 1) * (n + 1) + 1;
  4. 統計後續和個數最多的任務T數量相同的任務,T -> X * n -> T -> X * n -> T -> A -> B -> C。
class Solution {
    public int leastInterval(char[] tasks, int n) {
        if (tasks.length <= 1 || n < 1) return tasks.length;

        int st = 0;
        // 將任務按類型分組
        int[] cnts = new int[26];
        for (char task : tasks) {
            cnts[task - 'A']++;
        }
        // 將cnts按從小到大的順序排序,優先排列個數最多的任務
        Arrays.sort(cnts);
        int i = 25; // 個數最多的任務
        // 只排列個數最多的任務T,需要的時間至少爲 T -> X * n -> T -> X * n -> T
        st = (cnts[i--] - 1) * (n + 1) + 1;
        // 如果後面的任務有和最多的任務數量相同的話,則st + 1
        while (i >= 0 && cnts[i] == cnts[25]) {
            st++;
            i--;
        }
        return Math.max(st, tasks.length);
    }
}

時間複雜度: O(Math.max(tasks.length,26log26))O(Math.max(tasks.length, 26log26)),對長度爲26的數組排序;

空間複雜度: O(26)O(26)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章