leetcode 689. Maximum Sum of 3 Non-Overlapping Subarrays(3个非重叠子数组的最大和)

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.

给出一个数组,在数组中找到3个不重叠的子数组,每个子数组长度为k,使3个子数组的和最大,返回三个子数组的起始index

思路:
可分解成几个小问题,
第一步,分解问题,因为子数组的长度是k,那么中间子数组的起始index只能从k开始,到n-2k为止。如果知道了中间子数组的位置,左右两边子数组的范围也就知道了,这个时候如果知道了左右两边最大和的起始index,就相当于知道了3个子数组的起始index,问题得解。

对每一个中间子数组的位置,要能迅速得知左右两边子数组的最大和的起始index,就需要提前保存好每个范围内最大和对应的起始index,这就用到DP
而且如果已知起始index,因为子数组长度为k,就会知道结尾index,求这段长度内数组元素的和,会想到利用积分数组,即累加和数组

所以这道题用了(1)积分数组以方便求任意一段子数组的元素和,(2)DP用于保存最大和的起始index,及(3)slide window以遍历中间数组的位置

第二步,逐个解决小问题
(1) 先求一个积分数组。为了方便求前k个元素的和(需要用sum[k-1]-sum[-1],为了不考虑-1这个边界),在sum前面补一个0

(2) DP保存左右两边子数组最大和的起始index,先从左到右遍历,0~k-1这段不用考虑了,起始index都是0,从k开始到n-k+1,已知起始index,结尾index也就知道了,利用积分数组算这段长度内元素的和,一旦大于前面一个就更新保存index,否则就还是上一个保存的index
同理右边,从右到左遍历,从n-k到0,但是因为同样的和要保留较小的index,这里>=前一个值时就更新index

(3) 遍历中间数组的起始index,每个起始index都会对应左右两边子数组的最大和,把它们仨加起来,每遍历一个更新最大值和对应的3个子数组的起始index

参考方法

    public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
        if(nums == null || nums.length == 0 || k < 1) {
            return new int[]{};
        }
        
        int n = nums.length;
        int[] sum = new int[n+1];
        int[] left = new int[n];
        int[] right = new int[n];
        int total = 0;
        int[] result = new int[3];
        
        for(int i = 1; i <= n; i++) {
            sum[i] = sum[i-1] + nums[i-1];
        }
        
        total = 0;
        for(int i = k-1; i < n; i++) {
            int tmp = sum[i+1] - sum[i+1-k];          
            if(tmp > total) {
                total = tmp;
                left[i] = i-k+1;
            } else {
                left[i] = left[i-1];
            }
        }
        
        total = 0;
        for(int i = n-k; i >= 0; i--) {
            int tmp = sum[i+k] - sum[i];
            if(tmp >= total) {
                total = tmp;
                right[i] = i;
            } else {
                right[i] = right[i+1];
            }
        }
        
        total = 0;
        for(int i = k; i < n-2*k+1; i++) {
            int tmp = sum[left[i-1]+k] - sum[left[i-1]] + sum[i+k] - sum[i]
                + sum[right[i+k] + k] - sum[right[i+k]];
            if(tmp > total) {
                total = tmp;
                result[0] = left[i-1];
                result[1] = i;
                result[2] = right[i+k];
            } 
        }
        
        return result;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值