本题是动态规划的应用,和背包问题有些类似,但是其中有很多的细节值得借鉴和学习。
题目描述
给你一个整数数组 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;
}
}
网友评论