LeetCode-689 三個無重疊子陣列的最大和
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/maximum-sum-of-3-non-overlapping-subarrays
題目描述
給你一個整數陣列 nums 和一個整數 k ,找出三個長度為 k 、互不重疊、且3 * k 項的和最大的子陣列,並返回這三個子陣列。
以下標的陣列形式返回結果,陣列中的每一項分別指示每個子陣列的起始位置(下標從 0 開始)。如果有多個結果,返回字典序最小的一個。
示例 1:
輸入:nums = [1,2,1,2,6,7,5,1], k = 2
輸出:[0,3,5]
解釋:子陣列 [1, 2], [2, 6], [7, 5] 對應的起始下標為 [0, 3, 5]。
也可以取 [2, 1], 但是結果 [1, 3, 5] 在字典序上更大。
示例 2:
輸入:nums = [1,2,1,2,1,2,1,2,1], k = 2
輸出:[0,2,4]
提示:
1 <= nums.length <= 2 * 104
1 <= nums[i] <216
1 <= k <= floor(nums.length / 3)
解題思路
相信不少同學和我一樣,看到題的第一反應就是這道題要用滑動視窗的方法來解。
最初的想法是使用貪心演算法的思路,迴圈三次,每次找到沒有被提取出來的子串中最大的那一個,但是做的過程中發現這樣會破壞陣列的順序規律。比如nums = 1 6 8 7 7 1 8 1 k = 2的輸入下,根據題意最終找出來的子串應該是,68 77 18,但是在貪心演算法的思路下,就變成了87 18 16,出現這種情況的原因是用這種思路來找子串,僅僅考慮到了值最大這一條件,忽略了數字間的順序關係。
於是產生了想法2.0,三個滑動視窗,同時進行滑動,分別記錄每個視窗的和,如果三個視窗的和都是最大,那麼總和一定是最大的,而由於三個視窗依次排列,所以理論上找出的子串應該都是可以保持順序關係的,但是這種想法和第一種方法一樣,找到最大值後會導致視窗停下,還是隻考慮到區域性最大,並沒有考慮整體最大,並且,還可能三個視窗產生重疊。在更新一個視窗的時候,必須維護其他視窗,必須保證前視窗的尾部在後視窗的頭部之前。
想法3.0就產生了,放棄區域性的思想,將三個視窗間的關係利用總和來表示出了。首先將三個視窗分別設定為[0, k-1], [k, 2k-1], [2k, 3k-1],計算三個視窗分別的和,然後移動第一個視窗,如果移動後窗口的和大於原來的和,那麼移動第一個視窗並且更新最大值。在假設第一個視窗基礎上
並且由於滑動視窗是三個視窗同步向前的,並沒有停止的操作,所以保證了三個視窗不會發生重疊。
原始碼展示
class Solution { public: vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) { vector<int> iRet; int iSum[3] = { 0 }, iMax[3] = { 0 }, iIndex = 0, iIndex1 = 0, iIndex2 = k; iRet.resize(3); for(int i = 2 * k; i < nums.size(); i++) { iSum[0] += nums[i - 2 * k]; iSum[1] += nums[i - k]; iSum[2] += nums[i]; if(i >= 3 * k - 1) { if(iSum[0] > iMax[0]) { iMax[0] = iSum[0]; iIndex = i - 3 * k + 1; } if(iSum[1] + iMax[0] > iMax[1]) { iMax[1] = iSum[1] + iMax[0]; iIndex1 = iIndex; iIndex2 = i - 2 * k + 1; } if(iSum[2] + iMax[1] > iMax[2]) { iMax[2] = iMax[1] + iSum[2]; iRet = {iIndex1, iIndex2, i - k + 1}; } iSum[0] -= nums[i - 3 * k + 1]; iSum[1] -= nums[i - 2 * k + 1]; iSum[2] -= nums[i - k + 1]; } } return iRet; } };
執行結果