LeetCode 689. Maximum Sum of 3 Non-Overlapping Subarrays

Question

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).

Analysis

    题目的要求是找到3个长度为k的互补重叠的子数组,使得总和最大。从给出的例子可以看出,单个子数组的和不一定是和最大的子数组,只要3个的总和最大就可以。
    所以这个题目不应该从和的角度考虑,也就是说先计算出所有的长度为k的子数组的和然后从最大值开始进行组合的方法是费时费力的,不应该往这个思路去考虑。
    一个思路是从3的角度考虑。一般来说,
  • 对于要找到1个最值的题目,可以用二分法的思路思考;
  • 对于要找到2个数/子数组的题目,可以用两个指向首尾的指针相向移动,在线性的时间内求解;
  • 对于要找到3个数/子数组的题目,可以固定其中一个指针,另外两个指针相向移动,在O(n^2)的时间内求解。
    所以对于这个题目,一个直接的解法就是,固定中间长度为k的子数组,然后分别求解前后两段子数组中长度为k的子数组的最大和。

Solution 1

    这个解答是LeetCode官方给出的一个解,实现是Python代码。注意其中用到了几个提速的技巧:
  1. 预处理,先计算出了所有长度为k的子数组的和,免去不必要的重复计算,结果保存在W中。
  2. 然后在线性的时间内求解前i个子数组和的最大和与后i个子数组的最大值,分别保存在leftright中。
  3. 最后求解时,只需要固定中间的子数组的起点,然后从W中取中间子数组的和,从leftright中分别取第一个和第三个子数组的可能的最大和,加起来即固定中间的子数组的情况下,可能的最大和。
代码如下:
class Solution(object):
    def maxSumOfThreeSubarrays(self, nums, K):
        W = [] #array of sums of windows
        sum_ = 0
        for i, x in enumerate(nums):
            sum_ += x
            if i >= K: sum_ -= nums[i-K]
            if i >= K-1: W.append(sum_)

        left = [0] * len(W)
        best = 0
        for i in range(len(W)):
            if W[i] > W[best]:
                best = i
            left[i] = best

        right = [0] * len(W)
        best = len(W) - 1
        for i in range(len(W) - 1, -1, -1):
            if W[i] >= W[best]:
                best = i
            right[i] = best

        ans = None
        for j in xrange(K, len(W) - K):
            i, k = left[j-K], right[j+K]
            if ans is None or (W[i] + W[j] + W[k] >
                    W[ans[0]] + W[ans[1]] + W[ans[2]]):
                ans = i, j, k
        return ans

 Solution 2

    但是这个方法并不是最快的算法。还有一个很“奇技淫巧”的算法,只需要一遍遍历就可以求解的。基本思想是归纳,思路如下:
  1. 假设当前位置是i,已经知道了前i-2k个数中第一个长度为k的子数组最大和,设为max1,以及前i-k个数中前两个长度为k的子数组的最大和,设为max2,以及前i个数中所有三个长度为k的子数组的最大和,设为max3
  2. 那么在位置i+1,只需要拿起始位置为i-2k+1的子数组和max1比较,就可以求得前i-2k+1个数中第一个长度为k的子数组的最大和;同理,比较和更新max2max3
  3. 起始状态是i=2k,此时max1为起始位置为0的长度为k的子数组的和;max2为前2k个数的和,max3为前3k个数的和;
  4. 依次递推,当计算到i=nums.size()-k的时候,就可以得到最终答案了。
    C++实现的代码如下,同样的为了避免重复计算,提前算好了所有长度为k的子数组的和,保存在sumnums中。
class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k)
    {
        vector<int> id0(1, -1);
        vector<int> id1(2, -1);
        vector<int> id2(3, -1);
        
        vector<int> sumopt(3, -1);
        
        vector<int> sumnums;
        int sum = 0;
        for(int i = 0; i < k; ++i)
        {
            sum += nums[i];
        }
        sumnums.push_back(sum);
        for(int i = 0; i < nums.size() - k; ++i)
        {
            sum += nums[i + k] - nums[i];
            sumnums.push_back(sum);
        }
        
        sumopt[0] = sumnums[0];
        sumopt[1] = sumnums[k] + sumnums[0];
        sumopt[2] = sumnums[k + k] + sumopt[1];
        id0[0] = 0;
        id1[0] = 0; id1[1] = k;
        id2[0] = 0; id2[1] = k; id2[2] = k * 2;
        
        for(int i = k * 2; i < sumnums.size(); ++i)
        {
            if(sumnums[i - k * 2] > sumopt[0])
            {
                sumopt[0] = sumnums[i - k * 2];
                id0[0] = i - k * 2;
            }
            if(sumnums[i - k] + sumopt[0] > sumopt[1])
            {
                sumopt[1] = sumnums[i - k] + sumopt[0];
                id1[0] = id0[0]; id1[1] = i - k;
            }
            if(sumnums[i] + sumopt[1] > sumopt[2])
            {
                sumopt[2] = sumnums[i] + sumopt[1];
                id2[0] = id1[0]; id2[1] = id1[1]; id2[2] = i;
            }
        }
        return id2;
    }
};


展开阅读全文

没有更多推荐了,返回首页