[LeetCode] 621. Task Scheduler

題目鏈接: https://leetcode.com/problems/task-scheduler/description/

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);
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章