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

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

题目描述

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;
        
    }
}

你可能感兴趣的:(算法笔记之动态规划——三个无重叠子数组的最大和)