给定数组 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] 在字典序上更大。
注意:
nums.length的范围在[1, 20000]之间。
nums[i]的范围在[1, 65535]之间。
k的范围在[1, floor(nums.length / 3)]之间。
思路:我们可以将本题扩展到找到n个互补重叠的子数组的最大和,定义dp[i][j]:表示截止到nums[i]
形成的第j
个无重叠子数组的最大和,path[i][j]
代表截止到nums[i]
形成的第j
个无重叠子数组以哪个下标为结尾,用来回溯路径。
class Solution {
public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
return maxSumOfNSubarrays(nums,k,3);
}
private int[] maxSumOfNSubarrays(int[] nums,int k,int n) {
if(k<1 || k*n>nums.length) return new int[3];
int N=nums.length;
int s=0;
for(int i=0;i<k;i++)
s+=nums[i];
int[] sums=new int[N];
sums[k-1]=s;
for(int i=k;i<N;i++) {
s+=nums[i]-nums[i-k];
sums[i]=s;
}
int[][] dp=new int[N+1][n+1];
int[][] path=new int[N+1][n+1];
dp[k-1][1]=sums[k-1];
path[k-1][1]=k-1;
for(int i=k;i<N;i++)
for(int j=1;j<=n;j++) {
dp[i][j]=dp[i-1][j];
path[i][j]=path[i-1][j];
if(sums[i]+dp[i-k][j-1]>dp[i][j]) {
dp[i][j]=sums[i]+dp[i-k][j-1];
path[i][j]=i;
}
}
int len=0;
int[] res=new int [n];
int last=path[N-1][n];
res[len++]=last-k+1;
for(int i=n-1;i>0;i--) {
last=path[last-k][i];
res[len++]=last-k+1;
}
Arrays.parallelSort(res);
return res;
}
}