[LeetCode] Maximum Sum of 3 Non-Overlapping Subarrays 三個非重疊子數組的最大和


 

In a given array nums of positive integers, find three non-overlapping subarrays with maximum sum.

Each subarray will be of size k, and we want to maximize the sum of all 3*k entries.

Return the result as a list of indices representing the starting position of each interval (0-indexed). If there are multiple answers, return the lexicographically smallest one.

Example:

Input: [1,2,1,2,6,7,5,1], 2
Output: [0, 3, 5]
Explanation: Subarrays [1, 2], [2, 6], [7, 5] correspond to the starting indices [0, 3, 5].
We could have also taken [2, 1], but an answer of [1, 3, 5] would be lexicographically larger.

 

Note:

  • nums.length will be between 1 and 20000.
  • nums[i] will be between 1 and 65535.
  • k will be between 1 and floor(nums.length / 3).

 

這道題給了我們一個只包含正數的數組,讓我們找三個長度為k的不重疊的子數組,使得所有子數組的數字之和最大。首先我們應該明確的是,暴力搜索在這道題上基本不太可能,因為遍歷一個子數組的復雜度是平方級,遍歷三個還不得六次方啊,看OJ不削你~那么我們只能另辟蹊徑,對於這種求子數組和有關的題目時,一般都需要建立累加和數組,為啥呢,因為累加和數組可以快速的求出任意長度的子數組之和,當然也能快速的求出長度為k的子數組之和。因為這道題只讓我們找出三個子數組,那么我們可以先確定中間那個子數組的位置,這樣左右兩邊的子數組的位置范圍就縮小了,中間子數組的起點不能是從開頭到結尾整個區間,必須要在首尾各留出k個位置給其他兩個數組。一旦中間子數組的起始位置確定了,那么其和就能通過累加和數組快速確定。那么現在就要在左右兩邊的區間內分別找出和最大的子數組,遍歷所有的子數組顯然不是很高效,如何快速求出呢,這里我們需要使用動態規划Dynamic Programming的思想來維護兩個DP數組left和right,其中:

left[i]表示在區間[0, i]范圍內長度為k且和最大的子數組的起始位置

right[i]表示在區間[i, n - 1]范圍內長度為k且和最大的子數組的起始位置

這兩個dp數組各需要一個for循環來更新,left數組都初始化為0,前k個數字沒辦法,肯定起點都是0,變量total初始化為前k個數字之和,然后從第k+1個數字開始,每次向前取k個,利用累加和數組sums快速算出數字之和,跟total比較,如果大於total的話,那么更新total和left數組當前位置值,否則的話left數組的當前值就賦值為前一位的值。同理對right數組的更新也類似,total初始化為最后k個數字之和,然后從前一個數字向前遍歷,如果大於total,更新total和right數組的當前位置,否則right數組的當前值就賦值為后一位的值。一旦left數組和right數組都更新好了,那么就可以遍歷中間子數組的起始位置了,然后我們可以通過left和right數組快速定位出左邊和右邊的最大子數組的起始位置,並快速計算出這三個子數組的所有數字之和,用來更新全局最大值mx,如果mx被更新了的話,記錄此時的三個子數組的起始位置到結果res中,參見代碼如下:

 

class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        int n = nums.size(), mx = INT_MIN;
        vector<int> sums{0}, res, left(n, 0), right(n, n - k);
        for (int num : nums) sums.push_back(sums.back() + num);
        for (int i = k, total = sums[k] - sums[0]; i < n; ++i) {
            if (sums[i + 1] - sums[i + 1 - k] > total) {
                left[i] = i + 1 - k;
                total = sums[i + 1] - sums[i + 1 - k];
            } else {
                left[i] = left[i - 1];
            }
        }
        for (int i = n - 1 - k, total = sums[n] - sums[n - k]; i >= 0; --i) {
            if (sums[i + k] - sums[i] >= total) {
                right[i] = i;
                total = sums[i + k] - sums[i];
            } else {
                right[i] = right[i + 1];
            }
        }
        for (int i = k; i <= n - 2 * k; ++i) {
            int l = left[i - 1], r = right[i + k];
            int total = (sums[i + k] - sums[i]) + (sums[l + k] - sums[l]) + (sums[r + k] - sums[r]);
            if (mx < total) {
                mx = total;
                res = {l, i, r};
            }
        }
        return res;
    }
};

 

類似題目:

Best Time to Buy and Sell Stock III

 

參考資料:

https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C++Java-DP-with-explanation-O(n)

https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108246/C++-O(n)-time-O(n)-space-concise-solution

https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108230/Clean-Java-DP-O(n)-Solution.-Easy-extend-to-Sum-of-K-Non-Overlapping-SubArrays

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM