1. 程式人生 > 其它 >LeetCode-689 三個無重疊子陣列的最大和

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;
    }
};

執行結果