数组最大可以开多大_每日算法系列【LeetCode 689】三个无重叠子数组的最大和

5e56cabf61b30e8cf9c17f62b0dd2b82.png

题目描述

给定数组

equation?tex=nums 由正整数组成,找到三个互不重叠的子数组的最大和。 每个子数组的长度为
equation?tex=k ,我们要使这
equation?tex=3k 个项的和最大化。 返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。

示例1

输入:
[1,2,1,2,6,7,5,1], 2
输出:
[0, 3, 5]
解释:
子数组 [1, 2], [2, 6], [7, 5] 对应的起始索引为 [0, 3, 5]。
我们也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更大。

提示

  • equation?tex=nums.length 的范围在[1, 20000]之间。
  • equation?tex=nums%5Bi%5D 的范围在[1, 65535]之间。
  • equation?tex=k 的范围在 [1, floor(nums.legth / 3)]之间。

题解

首先看数据范围,这题不能使用暴力,暴力时间复杂度是

equation?tex=O%28n%5E3%29 ,一定会超时,所以考虑使用动态规划求解。

下面考虑一般情况,也就是求解划分成

equation?tex=N 个不重叠数组的最大和。

假设到第

equation?tex=i 个元素为止,一共已经产生了
equation?tex=j 个不重叠数组,那么令
equation?tex=dp%5Bi%5D%5Bj%5D 表示这
equation?tex=j个不重叠数组的最大和。

然后就要寻找状态转移方程。对于第

equation?tex=i 个元素,分为两种情况,可取可不取。

如果取,那就说明

equation?tex=nums%5Bi%5D 是第
equation?tex=j 个子数组的最后一个元素,那么转移方程为:

equation?tex=++++dp%5Bi%5D%5Bj%5D+%3D+dp%5Bi-k%5D%5Bj-1%5D+%2B+nums_%7Bi-k%2B1%3Ai%7D+%5C%5C

也就是说,从

equation?tex=i-k%2B1
equation?tex=i ,这
equation?tex=k 个元素构成了第
equation?tex=j 个子数组,那我们只需要求到第
equation?tex=i-k 个元素为止,产生
equation?tex=j-1 个不重叠数组的最大和即可。

如果不取,那问题就变成了求到第

equation?tex=i-1 个元素为止,产生
equation?tex=j 个不重叠数组的最大和,那么转移方程为:

equation?tex=++++dp%5Bi%5D%5Bj%5D+%3D+dp%5Bi-1%5D%5Bj%5D+++%5C%5C

当然这题还需要你还原出最大和的情况下,所有子数组的起始元素下标,所以需要另外用一个数组保存一下每一步的最优下标。

同样,假设到第

equation?tex=i 个元素为止,一共已经产生了
equation?tex=j 个不重叠数组,用
equation?tex=path%5Bi%5D%5Bj%5D 表示第
equation?tex=j 个子数组的末尾元素下标。

那么按照上面的推断,如果取第

equation?tex=i 个元素,那么
equation?tex=path%5Bi%5D%5Bj%5D%3Di ;否则的话
equation?tex=path%5Bi%5D%5Bj%5D%3Dpath%5Bi-1%5D%5Bj%5D

最后就是根据

equation?tex=path 数组还原答案了。

首先最后一个子数组的末尾元素下标一定是

equation?tex=idx%3Dpath%5Blen-1%5D%5BN%5D ,那么它的起始元素下标就是
equation?tex=idx-k%2B1 ,然后前一个子数组末尾元素下标就是
equation?tex=idx%3Dpath%5Bidx-k%5D%5BN-1%5D ,依次下去,直到第一个子数组被求解完毕。

代码

class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        int len = nums.size(), N = 3;
        int sum[len], s = 0;
        for (int i = 0; i < k; ++i) {
            s += nums[i];
            sum[i] = 0;
        }
        sum[k-1] = s;
        for (int i = k; i < len; ++i) {
            s += nums[i] - nums[i - k];
            sum[i] = s;
        }

        int dp[len][N+1], path[len][N+1];
        memset(dp, 0, sizeof dp);
        dp[k-1][1] = sum[k-1];
        path[k-1][1] = k - 1;
        for (int i = k; i < len; ++i) {
            for (int j = 1; j <= N; ++j) {
                dp[i][j] = dp[i-1][j];
                path[i][j] = path[i-1][j];
                if (dp[i][j] < dp[i-k][j-1] + sum[i]) {
                    dp[i][j] = dp[i-k][j-1] + sum[i];
                    path[i][j] = i;
                }
            }
        }
        
        vector<int> res;
        int idx = path[len-1][N];
        res.push_back(idx - k + 1);
        for (int i = N - 1; i > 0; --i) {
            idx = path[idx-k][i];
            res.push_back(idx - k + 1);
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

后记

d8156171418087e410e1081a212d38dd.png

可以看到,时间和空间还有提升的余地。想到的可能优化方法是类似于0-1背包那样,去掉动态规划数组的第二个维度,来优化空间复杂度。

但是这是有些问题的,暂时并没有想到不增加时间复杂度下减少空间开销的方法,欢迎大家提出自己的想法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值