1. 程式人生 > >[LeetCode] 621. Task Scheduler

[LeetCode] 621. Task Scheduler

Description

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 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].

解題思路

記 cooling interval 加 1 為一個排程週期,最優的順序應該為在每個排程週期內,依據task數量,從多到少無重複依次執行,若無法填滿整個排程週期,則剩下的空槽保留,開始下一輪排程,直到完成所有tasks

由於task的名字只可能為26個字母,並且結果與名字無關,因此用一個大小為26

的陣列儲存每種task的數量,先統計出每種task的數量,用數量代替每種task來繼續解題。

方法一:優先佇列

大體思路為用優先佇列模擬整個排程。宣告一個權值大優先順序高的優先佇列,保證出隊的順序為從大到小。將每種task統計出的數量入隊。並且,在每一輪中,用一個數組temp來記錄已經出隊且剩餘數量大於 1 的task,在該輪結束後重新入隊,以此來確保當輪排程無重複。

模擬每一輪排程,若佇列不為空則出隊,times加一,判斷出隊的task剩餘數量若不為 0,則存入陣列中,等待下一輪開始前重新入隊;若佇列為空,先將上一輪temp中的值重新入隊,並且統計上一輪剩下的空槽數量加到times中,再開始下一輪排程。直到所有tasks都完成,即佇列為空,返回times即為結果。

方法二:統計空閒槽

例子,tasks = ["A","A","A","B","B","B","C"], n = 2

task數最大值為max_val,空槽數最多為(max_val - 1) * n,記為idles

A - -
A - -
A

實際空槽數為最大空槽數減去其他task在除最後一輪以外的輪次中佔用的槽數

A B C
A B -
A B

因此,這種方法可以先計算出最大空槽數idles = (max_val - 1) * n,然後對剩餘的每一種task,用idles - min(task_size, max_val - 1)。另外,n 可能為 0,這樣統計出的空槽數可能為負數,所以需要先判斷空槽數是否大於 0,若大於則結果為空槽數加上tasks總數,否則為tasks總數。

方法三:優化版

與方法二類似,換了一種思路。不用統計空槽數,因為除了最後一輪以外的輪次所需時間肯定為n + 1,這些輪次總共需要時間為(max_val - 1) * (n + 1),重點在最後一個輪次。最後一輪中,只有數量等於max_valtask才會在該輪中排程,因此只需要統計出數量等於max_valtask數量,用前面輪次的總時間加上這個數量即為最終結果。

另外,還要考慮特殊情況,若 n 為 0,則上述方法統計出的結果會小於tasks總數。

舉個例子,tasks = ["A","A","A","B","B","B"], n = 0

上述方法計算出的結果為 (3 - 1) * (0 + 1) + 2 = 4,而正確結果應該是tasks總數 6,因此最終結果應該為該方法算出的結果與tasks總數之間的較大值。

Code

方法一:優先佇列

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        int times = 0, i = n + 1;
        priority_queue<int, vector<int>, less<int>> pq;
        vector<int> alpha(26);

        for (char &ch: tasks) {
            alpha[ch - 'A']++;
        }

        for (int count: alpha) {
            if (count > 0) {
                pq.push(count);
            }
        }

        while (!pq.empty()) {
            times += n - i + 1;

            i = 0;
            list<int> temp;

            while (i <= n) {
                if (!pq.empty()) {
                    if (pq.top() > 1) {
                        temp.push_back(pq.top() - 1);
                    }
                    pq.pop();
                    times++;
                } else {
                    break;
                }

                i++;
            }

            for (auto count: temp) {
                pq.push(count);
            }
        }

        return times;
    }
};

方法二:統計空閒槽

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        vector<int> alpha(26);

        for (char &ch: tasks) {
            alpha[ch - 'A']++;
        }

        sort(alpha.begin(), alpha.end());

        int max_val = alpha.back() - 1;
        int idles = max_val * n;

        for (int i = 24; i >= 0 && alpha[i] > 0; --i) {
            idles -= min(max_val, alpha[i]);
        }

        return idles > 0 ? idles + tasks.size() : tasks.size();
    }
};

方法三:優化版

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        vector<int> alpha(26);
        for (char &ch: tasks) {
            alpha[ch - 'A']++;
        }
        sort(alpha.begin(), alpha.end());

        int i = 25, max_val = alpha.back(), len = tasks.size();
        while (i >= 0 && alpha[i] == max_val) --i;

        return max(len, (max_val - 1) * (n + 1) + 25 - i);
    }
};