LeetCode Notes_#1031_两个非重叠子数组的最大和

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:动态规划

参考C++ 动态规划+滑动窗口 O(n)

思路

考虑题意: 必然存在一条分界线把 A 拆分成两半,存在两大类情况:

  1. 长度为 L 的连续子数组在左边, 长度为 M 的连续子数组在右边
  2. 或者反过来长度为 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的数组

posted @ 2021-05-08 15:55  Howfar's  阅读(61)  评论(0编辑  收藏  举报