美文网首页
算法笔记之动态规划——三个无重叠子数组的最大和

算法笔记之动态规划——三个无重叠子数组的最大和

作者: 简单一点点 | 来源:发表于2021-12-09 14:17 被阅读0次

    本题是动态规划的应用,和背包问题有些类似,但是其中有很多的细节值得借鉴和学习。

    题目描述

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

    给你一个整数数组 nums 和一个整数 k ,找出三个长度为 k 、互不重叠、且 3 * k 项的和最大的子数组,并返回这三个子数组。

    以下标的数组形式返回结果,数组中的每一项分别指示每个子数组的起始位置(下标从 0 开始)。如果有多个结果,返回字典序最小的一个。

    思路分析

    看到题目,首先能想到动态规划,设 dp[i][j] 其中i为最后一个数组的初始元素索引,j为当前最大数组数量。状态转移方程为:

    dp[i][j] = max(dp[i - 1][j], dp[i - k][j - 1] + nums[i] +...+ nums[i + k - 1])

    其中 nums[i] +...+ nums[i + k - 1] 的子数组之和可以使用前缀和来优化求出。

    另外需要注意的一点是本题需要求出路径,而不是最大值,因此动态规划完成之后需要反推路径,这里要求字典序最小,因此状态转移到dp[i][j]时需要优先考虑dp[i - 1][j]。

    Java代码

    class Solution {
        public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
            // 前缀和 注意前面有个0
            long[] prefixSum = new long[nums.length + 1];
            for(int i = 0; i < nums.length; i++) {
                prefixSum[i + 1] = prefixSum[i] + (long)nums[i];
            }
            long[][] dp = new long[nums.length][4];
            for(int i = 0; i <= nums.length - k; i++) { 
                for(int j = 1; j < 4; j++) { // j为子数组数量
                    long a1 = i >= 1 ? dp[i - 1][j] : 0;
                    long a2 = i >= k ? dp[i - k][j - 1]: 0;
                    dp[i][j] = Math.max(a1, a2 + prefixSum[i + k] - prefixSum[i] );
                }
            }
    
            int[] r = new int[3];
            // 回溯查找
            int j = 3;
            int i = nums.length - k;
            while(j > 0) {
                //优先找 dp[i - 1][j]
                if(i >= 1 && dp[i - 1][j] == dp[i][j]) {
                    i--;
                } else {
                    r[j - 1] = i;
                    i -= k;
                    j--;
                }
            }
            return r;
            
        }
    }
    

    相关文章

      网友评论

          本文标题:算法笔记之动态规划——三个无重叠子数组的最大和

          本文链接:https://www.haomeiwen.com/subject/zeeffrtx.html