给定数组arr和k,求3个不重叠的长为k的子数组的累加和之和最大值是多少?

给定数组arr和k,求3个不重叠的长为k的子数组的累加和之和最大值是多少?

提示:这个题不是数组三连问题
本题的两个重要知识点:
(1)数组arr中必须以i位置结尾的子数组,其最大累加和是多少?
https://blog.csdn.net/weixin_46838716/article/details/124375762?spm=1001.2014.3001.5502
(1)又是(2)的基础:
(2)数组arr的0–i范围上任选一个子数组的最大累加和是多少?
https://blog.csdn.net/weixin_46838716/article/details/124377952?spm=1001.2014.3001.5502


题目

给定数组arr和k,寻找3个不重叠的子数组,每个子数组的长为k,每个子数组累加和之和的最大值是多少?


一、审题

示例:arr=1,2,1,2,6,7,5,1
k=2
找3个长为2的子数组
他们各自累加和最大
再求和
在这里插入图片描述


二、解题

不重叠,找3个子数组,显然是要枚举的
但是不能每次枚举,都去单独求每个子数组内部的累加和
显然,我们又遇到了一个范围上求连续k长度的子数组累加和,所以必然要用前缀和数组加速
比如
L–R内的累加和是多少?
如果preSum[i]代表[0,i)上的累加和,注意,左闭右开,这是经常用到的,不包含i位置,故需要N+1长度
在这里插入图片描述
这样的话,我们可以快速拿到L–R内的累加和

这么思考本题:也是本题的解题大流程
要找3个子数组,我们以中间那个数组为基准,枚举所有可能的情况:

(1)不妨设中间k长度的子数组和为part2,它占据的位置是i–i+k-1
(2)则0–i-1范围上咱可以任意找一个子数组(长度为k),其累加和最大为part1;
(3)同理,在i+k–N-1范围上,我们可以任意找一个子数组(长度为k),其累加和最大为part3;

通过枚举part2的位置i,至少从k开始枚举,最后一个位置是,N-2k
为啥最后一个位置N-2k?
因为,part3占据k长度,part2也要k长度,就长2k
N-2k–N-1就是2k长度
在这里插入图片描述
每次枚举一个位置的话,我们只需要枚举o(n)的复杂度
而每次枚举,我们希望能以o(1)速度拿到part1,part2,part3
刚刚说过,前缀累加和,可以快速拿到part2:sum[i+k]-sum[i]=sum[i–i+k-1]

那么如何快速拿到part1和part3呢?

——先来看如何快速拿到part1
之前讲过的基础知识:
数组arr的0–i范围上任选一个子数组的最大累加和是多少?
https://blog.csdn.net/weixin_46838716/article/details/124377952?spm=1001.2014.3001.5502
看看,part1可不就是找0–i位置上找任意一个子数组,求其最大累加和,这里要注意,唯一的区别就是:
本题中我们的任意子数组,长度必须是k。【这是限定条件】

这里啊,我们一样,讨论长为k的子数组的最大累加和,是不是以i结尾?
(1)是以i结尾,那不扯只含i,或者既包含i又包含前面的元素,咱现在就是i及前面k个数的累加和;
(2)不是以i结尾,那就是说之前求过的某个最大值就行
故,定义dp[i]是0–i范围内,任意长度为k的子数组的最大累加和;
咋求呢?这样:
先整一个前缀累加和:sumFromLeft,从左往右的前缀累加和【上面说得很清楚】
然后,定义个数组dpLeft也就是这里的dp
每一个dpLeft[i]咋算呢?

dpLeft[i] = Math.max(dpLeft[i - 1], sumFromLeft[i + 1] - sumFromLeft[i - k + 1]);

怎么理解,dpLeft[i - 1]就是不以i结尾的子数组,前面0–i-1范围上随便找一个即可
跟是谁比呢?自然是必须以i结尾的那个子数组
sumFromLeft[i + 1] - sumFromLeft[i - k + 1])就是i–i+k范围内的长为k的子数组累加和
这样的话dpLeft就准备好了
到时候一旦调:dpLeft[i],就o(1)速度拿到0–i范围内,任意长度为k的子数组的最大累加和。

——同理,咱们怎么如何快速拿到part3
故,定义dpRight[i]是i–N-1范围内,任意长度为k的子数组的最大累加和;
咋求呢?这样:
先整一个前缀累加和:sumFromRight,从右往左的前缀累加和【对比part1就知道,上面说得很清楚】
如果i从0开始算,则下标也很好推:

sumFromRight[N - 1 - i] = sumFromRight[N - i] + arr[i];//右边加过来

然后,定义个数组dpRight也就是这里的dp
每一个dpRight[i]咋算呢?

dpRight[i] = Math.max(dpRight[i + 1], sumFromRight[i - 1] - sumFromRight[i + k - 1]);

怎么理解,dpRight[i + 1]就是不以i开头的子数组,后面,i+1–N-1范围上随便找一个即可
跟是谁比呢?自然是必须以i开头的那个子数组
sumFromRight[i - 1] - sumFromLeft[i - k + 1])就是i–i+k范围内的长为k的子数组累加和
这样的话dpRight就准备好了
到时候一旦调:dpRight[i],就o(1)速度拿到i–N-1范围内,任意长度为k的子数组的最大累加和。

有了part2
有了dpLeft数组
有了dpRight数组
咱下面就可以枚举part2的所有情况了
每次三个累加和再求和,更新max即可,也就是本题的解法:

public static int maxSumOf3SubArray(int[] arr, int k){
        if (arr == null || arr.length < 3 * k) return 0;
        //i,长度为k的子数组累加和是多少????part2
        int N = arr.length;
        int[] sumFromLeft = new int[N + 1];
        int[] sumFromRight = new int[N + 1];
        sumFromLeft[0] = 0;
        sumFromLeft[N] = 0;//没开始之前都是0
        for (int i = 0; i < N; i++) {
            sumFromLeft[i + 1] = sumFromLeft[i] + arr[i];
            sumFromRight[N - 1 - i] = sumFromRight[N - i] + arr[i];//右边加过来
        }

        //首先解决一下0--i位置上的任意子数组,长度为k,它的最大累加和是多少????dpLeft
        int[] dpLeft = new int[N];
        for (int i = 0; i < k; i++) {
            dpLeft[k - 1] += arr[i];//连续k个,只需要记录k-1位置
        }
        for (int i = k; i < N; i++) {
            dpLeft[i] = Math.max(dpLeft[i - 1], sumFromLeft[i + 1] - sumFromLeft[i - k + 1]);
            // i开始往前数k个
        }

        //首先解决一下i--N-1位置上的任意子数组,长度为k,它的最大累加和是多少????dpRight
        int[] dpRight = new int[N];
        for (int i = N - 1; i > N - 1 - k; i--) {
            dpRight[N - k] += arr[i];//连续加k次,只需要记录N-k这个位置
        }
        for (int i = N - 1 - k; i > 0; i--) {
            dpRight[i] = Math.max(dpRight[i + 1], sumFromRight[i - 1] - sumFromRight[i + k - 1]);
        }

        int max = 0;
        //枚举中间数组-----大流程核心思想
        for (int i = k; i <= N - 2*k; i++) {
            //i是中间区间的起点:
            // 得预留左边0--k-1,所以从k开始
            //右边还得留至少k长度,中间自己长为k,所以右边至少起点最大是N-2k[坐标是0--N-1],恰好能取到N-2k
            //坐标能换算清楚
            int part2 = sumFromLeft[i + k] - sumFromLeft[i];//中间,包含位置i
            int part1 = dpLeft[i - 1];//左边这部分中最大累加和
            int part3 = dpRight[i + k - 1];//右边那部分中最大累加和--coding清楚坐标位置
            max = Math.max(max, part1 + part2 + part3);
        }

        return max;
    }

    public static void test(){
        int[] arr = {1,2,1,2,6,7,5,1};
        int k = 2;
        System.out.println(maxSumOf3SubArray(arr, k));
    }

总结

提示:重要经验:

1)熟悉2大数组累加和的基础,对于理解和解决本题,至关重要
2)用预设前缀累加和数组,可以快速拿到L–R内的累加和
3)准备好0–i范围内任意长度为k的子数组的最大累加和数组;
4)准备好i–N-1范围内任意长度为k的子数组最大累加和数组;
可以快速帮助破解本题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰露可乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值