LeetCode Notes_#1031_两个非重叠子数组的最大和
Contents
题目
解答
方法1:暴力搜索
class Solution {
public int maxSumTwoNoOverlap(int[] A, int L, int M) {//[0,6,5,2,2,5,1,9,4], L = 1, M = 2
int[] pre_sum = new int[A.length];
pre_sum[0] = A[0];
for(int i = 1; i < A.length; i++){
pre_sum[i] = pre_sum[i - 1] + A[i];//[0,6,11,13,15,20,21,30,34]
}
int max_sum = 0;
//对于每个[i...i+L-1],遍历所有其他的[j...j+M-1]
for(int i = 0; i <= A.length - L; i++){//i = 0...8
for(int j = 0; j <= A.length - M; j++){//j = 0...7
//判断是否重叠,如果重叠,直接跳过
if(isOverlap(i, L, j, M)) continue;//j = 0, 1
if(calSubSum(j, M, pre_sum) == -1 || calSubSum(i, L, pre_sum) == -1) continue;
int sum = calSubSum(i, L, pre_sum) + calSubSum(j, M, pre_sum);//重置
System.out.println(sum + " " + i + " " + j);
if(sum > max_sum) max_sum = sum;
}
}
return max_sum;
}
//判断是否重叠:可以直接遍历比较,或者通过区间判断
private boolean isOverlap(int i, int L, int j, int M){
// for(int m = i; m <= i + L - 1; m++){
// if(m >= j && m <= j + M - 1) return true;
// }
// return false;
if(i > j + M - 1 || j > i + L - 1) return false;//上界>另一个的下界,就说明是不重叠的
return true;
}
private int calSubSum(int start, int len, int[] pre_sum){
if(start < 0 || start + len - 1 >= pre_sum.length) return -1;//-1表示非法范围,主函数应该直接跳过这种情况
if(start == 0) return pre_sum[start + len - 1];//pre[0+1-1] = 0
else return pre_sum[start + len - 1] - pre_sum[start - 1];
}
}
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n)
,前缀和数组
方法2:动态规划
思路
考虑题意: 必然存在一条分界线把 A 拆分成两半,存在两大类情况:
- 长度为 L 的连续子数组在左边, 长度为 M 的连续子数组在右边
- 或者反过来长度为 M 的连续子数组在左边, 长度为 L 的连续子数组在右边
引入
- dp[i][0]: 从 A[0]-A[i] 连续 L 长度子数组最大的元素和
- dp[i][1]: 从 A[0]-A[i] 连续 M 长度子数组最大的元素和
- dp[i][2]: 从 A[i]-A[A.size()-1] 连续 L 长度子数组最大的元素和
- dp[i][3]: 从 A[i]-A[A.size()-1] 连续 M 长度子数组最大的元素和
某些超出范围的下标, 值设置为 0 (默认值)
代码中首先用滑动窗口计算了 dp, 然后将 dp 分成两组, 计算两大类情况下的结果,取最大值返回即可
代码
class Solution {
public int maxSumTwoNoOverlap(int[] A, int L, int M) {
int[][] dp = new int[A.length][4];
int presum = 0;
int maxsum;
//dp[i][0]:A[i]之前的L数组的最大和
for(int i = 0; i < L; i++) presum += A[i];
maxsum = presum;
dp[L - 1][0] = maxsum;
for(int i = L; i < A.length; i++){
presum -= A[i - L];
presum += A[i];
maxsum = Math.max(presum, maxsum);
dp[i][0] = maxsum;
}
//dp[i][1]:A[i]之前的M数组的最大和
presum = 0;
for(int i = 0; i < M; i++) presum += A[i];
maxsum = presum;
dp[M - 1][1] = maxsum;
for(int i = M; i < A.length; i++){
presum -= A[i - M];
presum += A[i];
maxsum = Math.max(presum, maxsum);
dp[i][1] = maxsum;
}
//dp[i][2]:A[i]之后的L数组的最大和
int sufsum = 0;
for(int i = A.length - 1; i >= A.length - L; i--) sufsum += A[i];
maxsum = sufsum;
dp[A.length - L][2] = maxsum;
for(int i = A.length - L - 1; i >= 0; i--){
sufsum -= A[i + L];
sufsum += A[i];
maxsum = Math.max(sufsum, maxsum);
dp[i][2] = maxsum;
}
//dp[i][3]:A[i]之后的M数组的最大和
sufsum = 0;
for(int i = A.length - 1; i >= A.length - M; i--) sufsum += A[i];
maxsum = sufsum;
dp[A.length - M][3] = maxsum;
for(int i = A.length - M - 1; i >= 0; i--){
sufsum -= A[i + M];
sufsum += A[i];
maxsum = Math.max(sufsum, maxsum);
dp[i][3] = maxsum;
}
//计算最终结果:dp[i-1][0] + dp[i][3]的最大,dp[i-1][1]+dp[i][2]的最大,找二者中最大
int res = 0;
for(int i = L; i <= A.length - M; i++){
res = Math.max(res, dp[i-1][0] + dp[i][3]);
}
for(int i = M; i <= A.length - L; i++){
res = Math.max(res, dp[i - 1][1] + dp[i][2]);
}
return res;
}
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(n)
,额外用了4个长度为n的数组