[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]
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; } };
類似題目:
參考資料: