力扣 689. 三个无重叠子数组的最大和

题目来源:https://leetcode-cn.com/problems/maximum-sum-of-3-non-overlapping-subarrays/

大致题意:
给一个数组 nums 和一个整数 k,在数组中找到互不重叠的三个长度为 k 的子数组,求出三个子数组可能有的最大和。返回最大和对应的三个子数组的左边界索引,如果有多个答案,返回字典序最小的

思路

一看是困难题我就怂了…
没想到今天的官方题解写的太棒,看了一下就懂了
这里就用自己的理解再写一遍

滑动窗口

题目中规定了子数组的长度为 k,那么显然可以使用滑动窗口:窗口大小为 k

这道题可以拆成子问题:在数组中找到一个长度为 k 的子数组的最大值、在数组中找到两个长度为 k 的子数组的最大值、在数组中找到三个长度为 k 的子数组的最大值

首先看如何在数组中找到一个长度为 k 的子数组的最大值:

  • 使用长度为 k 的滑动窗口在数组上滑动,比较当前窗口内所有元素的和 sum1 与之前存的最大和 maxSum1,若当前和更大,那么更新最大和以及最大和对应的窗口左边界索引 maxSum1Idx

接下来看在数组中找到两个长度为 k 的子数组的最大值:

  1. 使用长度为 k 的滑动窗口在数组上滑动(起始位置为 0),比较当前窗口内所有元素的和 sum1 与之前存的最大和 maxSum1,若当前和更大,那么更新最大和以及最大和对应的窗口左边界索引 maxSum1Idx
  2. 使用长度为 k 的滑动窗口在数组上滑动(起始位置为 k),比较当前窗口内所有元素的和 sum2 加上上一步存的一个子数组的最大和 maxSum1之前存的两个子数组最大和 maxSum12,若当前和更大,那么更新最大和以及最大和对应的两个窗口左边界索引 maxSum12Idx1 和 maxSum12Idx2
  3. 同时滑动这两个窗口

那么同样的,可以求出三个长度为 k 的子数组的最大值:

  1. 使用长度为 k 的滑动窗口在数组上滑动(起始位置为 0),比较当前窗口内所有元素的和 sum1 与之前存的最大和 maxSum1,若当前和更大,那么更新最大和以及最大和对应的窗口左边界索引 maxSum1Idx
  2. 使用长度为 k 的滑动窗口在数组上滑动(起始位置为 k),比较当前窗口内所有元素的和 sum2 加上上一步存的一个子数组的最大和 maxSum1之前存的两个子数组最大和 maxSum12,若当前和更大,那么更新最大和以及最大和对应的两个窗口左边界索引 maxSum12Idx1 和 maxSum12Idx2
  3. 使用长度为 k 的滑动窗口在数组上滑动(起始位置为 2k),比较当前窗口内所有元素的和 sum3 加上上一步存的两个子数组的最大和 maxSum12之前存的两个子数组最大和 maxTotal,若当前和更大,那么更新最大和以及最大和对应的三个窗口左边界索引

代码:

public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
        int[] ans = new int[3];
        int n = nums.length;
        int sum1 = 0, maxSum1 = 0, maxSum1Idx = 0;
        int sum2 = 0, maxSum12 = 0, maxSum12Idx1 = 0, maxSum12Idx2 = 0;
        int sum3 = 0, maxTotal = 0;
        for (int i = 2 * k; i < n; i++) {
            // 更新窗口中三个子数组的和
            sum1 += nums[i - 2 * k];
            sum2 += nums[i - k];
            sum3 += nums[i];
            // 窗口已经形成
            if (i >= 3 * k - 1) {
                // 当前窗口的 一个子数组和 大于已有的最大值,更新
                if (sum1 > maxSum1) {
                    maxSum1 = sum1;
                    maxSum1Idx = i - 3 * k + 1;
                }
                // 当前窗口的 两个子数组和 大于已有的最大值,更新
                if (maxSum1 + sum2 > maxSum12) {
                    maxSum12 = maxSum1 + sum2;
                    maxSum12Idx1 = maxSum1Idx;
                    maxSum12Idx2 = i - 2 * k + 1;
                }
                // 当前窗口的 三个子数组和 大于已有的最大值,更新
                if (maxSum12 + sum3 > maxTotal) {
                    maxTotal = maxSum12 + sum3;
                    ans[0] = maxSum12Idx1;
                    ans[1] = maxSum12Idx2;
                    ans[2] = i - k + 1;
                }
                // 滑动窗口,减去左边界的值
                sum1 -= nums[i - 3 * k + 1];
                sum2 -= nums[i - 2 * k + 1];
                sum3 -= nums[i - k + 1];
            }
        }
        return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

三更鬼

谢谢老板!

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

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

打赏作者

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

抵扣说明:

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

余额充值