[Leetcode]689.Maximum Sum of 3 Non-Overlapping Subarrays

链接:LeetCode689

给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。

每个子数组的长度为k,我们要使这3*k个项的和最大化。

返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。

示例:

输入: [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] 在字典序上更大。

相关标签:动态规划

又是一道明确是用动态规划解法后,便不难想出动态转移方程的题目。
下面考虑一般情况,也就是求解划分成N个不重叠数组的最大和。
假设到第i个元素为止,一共已经产生了j个不重叠数组,那么令$ dp[i][j] ji$个元素,分为两种情况,可取可不取。**
如果取,那就说明 nums[i] 是第j个子数组的最后一个元素,那么转移方程为:

dp[i][j]=dp[ik][j1]+numsik+1:i

也就是说,从ik+1i,这k个元素构成了第j个子数组,那我们只需要求到第ik个元素为止,产生j1个不重叠数组的最大和即可。
如果不取,那问题就变成了求到第i1个元素为止,产生j个不重叠数组的最大和,那么转移方程为:

dp[i][j]=dp[i1][j]

当然这题的难度还在于,需要你还原出最大和的情况下,所有子数组的起始元素下标,所以需要另外用一个数组保存一下每一步的最优下标。
同样,假设到第i个元素为止,一共已经产生了j个不重叠数组,用path[i][j]表示第j个子数组的末尾元素下标。
那么按照上面的推断,如果取第i个元素,那么path[i][j]=i;否则的话 path[i][j]=path[i1][j]。最后就是根据 path 数组还原答案了。

其代码如下:

python:

class Solution:
    def maxSumOfThreeSubarrays(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        dp = [[0 for j in range(4)] for i in range(n+1)]
        path = [[0 for j in range(4)] for i in range(n+1)]
        cur = sum(nums[:k])
        sums = [cur]
        for i in range(k,n):
            cur += nums[i] - nums[i-k]
            sums.append(cur)

        for i in range(1,n+1):
            for j in range(1,4):
                if i < j*k:
                    dp[i][j] = 0
                    continue
                dp[i][j] = dp[i-1][j]
                path[i][j] = path[i-1][j]
                if dp[i-k][j-1] + sums[i-k] > dp[i][j]:
                    dp[i][j] = dp[i-k][j-1] + sums[i-k]
                    path[i][j] = i-k

        res = []
        ind = n
        for j in reversed(range(1,4)):
            res.append(path[ind][j])
            ind = path[ind][j]
        res.reverse()
        return res

C++:

class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<vector<int>> dp(n+1,vector<int>(4,0));
        vector<vector<int>> path(n+1,vector<int>(4,0));
        int cur = accumulate(nums.begin(),nums.begin()+k,0);
        vector<int> sums{cur};
        for(int i=k;i<n;++i){
            cur += nums[i]-nums[i-k];
            sums.push_back(cur);
        }
        for(int i=1;i<n+1;++i){
            for(int j=1;j<4;++j){
                if(i<j*k){
                    continue;
                }
                dp[i][j] = dp[i-1][j];
                path[i][j] = path[i-1][j];
                if(dp[i-k][j-1]+sums[i-k]>dp[i][j]){
                    dp[i][j] = dp[i-k][j-1]+sums[i-k];
                    path[i][j] = i-k;
                }
            }
        }
        vector<int> res;
        int ind = n;
        for (int j=3;j>0;--j){
            res.insert(res.begin(),path[ind][j]);
            ind = path[ind][j];
        }
        return res;
    }
};

参考:[LeetCode 689]三个无重叠子数组的最大和

posted @   Jamest  阅读(146)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示