LCP 12. 小張刷題計劃

incipe發表於2020-10-02

原題

為了提高自己的程式碼能力,小張制定了 LeetCode 刷題計劃,他選中了 LeetCode 題庫中的 n 道題,編號從 0 到 n-1,並計劃在 m 天內按照題目

編號順序刷完所有的題目(注意,小張不能用多天完成同一題)。

在小張刷題計劃中,小張需要用 time[i] 的時間完成編號 i 的題目。此外,小張還可以使用場外求助功能,通過詢問他的好朋友小楊題目的解法,

可以省去該題的做題時間。為了防止“小張刷題計劃”變成“小楊刷題計劃”,小張每天最多使用一次求助。

我們定義 m 天中做題時間最多的一天耗時為 T(小楊完成的題目不計入做題總時間)。請你幫小張求出最小的 T是多少。

示例 1:

輸入:time = [1,2,3,3], m = 2

輸出:3

解釋:第一天小張完成前三題,其中第三題找小楊幫忙;第二天完成第四題,並且找小楊幫忙。這樣做題時間最多的一天花費了 3 的時間,並且這個值是最小的。

示例 2:

輸入:time = [999,999,999], m = 4

輸出:0

解釋:在前三天中,小張每天求助小楊一次,這樣他可以在三天內完成所有的題目並不花任何時間。

限制:

1 <= time.length <= 10^5

1 <= time[i] <= 10000

1 <= m <= 1000

來源:力扣(LeetCode)

連結:https://leetcode-cn.com/problems/xiao-zhang-shua-ti-ji-hua

思路

Ⅰ. 這是一類題

最大化最小值,最大化平均值,最小化最大值…

如何理解最大化最小值問題和最小化最大值問題

這類題主要的方法就是二分答案/搜尋。

Ⅱ. 類似的題目

875. 愛吃香蕉的珂珂

1482. 製作 m 束花所需的最少天數

410. 分割陣列的最大值

1552. 兩球之間的磁力

Ⅲ. 程式碼

  • C++程式碼
class Solution {
private:
    bool check(int mid, int m, vector<int> &time) {
        // 所用天數, 總用時, 題耗時最多
        // 這裡用了貪心的思想, 耗時最多的那個題目, 就場外求助
        int days = 1, total = 0, maxCost = 0;
        // 每天的場外求助是否被使用
        bool use = true;
        int size = time.size();
        for (int i = 0; i < size; ++i) {
            // 維護那個耗時最長的題的花費時間
            maxCost = max(maxCost, time[i]);
            // 總用時
            total += time[i];
            // 如果大於我給定的時間了
            if (total > mid) {
                // 如果有場外求助, 說明我今天還可以多解決一道題
                if (use) {
                    total -= maxCost;
                    use = false;
                } else {
                    // 如果沒有場外求助, 開啟下一天, 所有資訊清空
                    ++days;
                    total = 0;
                    maxCost = 0;
                    --i;
                    use = true;
                }
            }
            // 如果天數大於m了, 說明一天只有mid的時間不夠, 返回false
            if (days > m) {
                return false;
            }
        }
        return true;
    }
public:
    int minTime(vector<int>& time, int m) {
        int left = 0, right = 0;
        // 左邊界=>0, 右邊界=>所有題的總時間
        for (auto &it : time) {
            right += it;
        }
        while (left <= right) {
            int mid = left + (right - left) / 2;
            // 每天用mid的時間可以完成嗎?
            if (check(mid, m, time)) {
                // 如果可以完成, 說明時間還可以減少
                right = mid - 1;
            } else {
                // 如果不能完成, 說明時間不夠
                left = mid + 1;
            }
        }
        return left;
    }
};
  • Python程式碼
class Solution:
    def check(self, mid, m, time):
        use = True
        days, total, maxCost = 1, 0, 0
        size = len(time)
        i = 0
        while i < size:
            maxCost = max(maxCost, time[i])
            total += time[i]
            if total > mid:
                if use:
                    total -= maxCost
                    use = False
                else:
                    days += 1
                    i -= 1
                    use, total, maxCost = True, 0, 0
            i += 1
            if days > m:
                return False
        return True

    def minTime(self, time: List[int], m: int) -> int:
        left, right = 0, sum(time)
        while left <= right:
            mid = (left + right) // 2
            if self.check(mid, m, time):
                right = mid - 1
            else:
                left = mid + 1
        return left

特別注意:Python中在 for in range(size) 中修改i的值是沒有作用的,也強烈建議不要這樣子做

原因是因為 for 迴圈每次結束 i 都會被重新賦值。

Why can’t you modify lists through “for in” loops in Python?

總結

還是要多練習,做多了就好了,其餘幾道題解後面補上~

相關文章