【线性 dp】B011_LC_删除一次得到子数组最大和(分类讨论 / 空间压缩)

一、Problem

给你一个整数数组,返回它的某个 非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。

换句话说,你可以从原数组中选出一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。

注意,删除一个元素后,子数组 不能为空。

输入:arr = [1,-2,0,3]
输出:4
解释:我们可以选出 [1, -2, 0, 3],然后删掉 -2,这样得到 [1, 0, 3],和最大。

输入:arr = [1,-2,-2,3]
输出:3
解释:我们直接选出 [3],这就是最大和。

二、Solution

方法一:dp

  • 定义状态
    • f [ i ] [ 0 ] f[i][0] 表示以 a [ i ] a[i] 结尾且没删过元素的子数组最大总和
    • f [ i ] [ 1 ] f[i][1] 表示以 a [ i ] a[i] 结尾且删除过元素的子数组最大总和
  • 思考初始化:
    • f [ 0 ] [ 0 ] = a [ 0 ] f[0][0] = a[0]
    • f [ 0 ] [ 1 ] = 0 f[0][1] = 0
  • 思考状态转移方程
    • f [ i ] [ 0 ] = m a x ( f [ i 1 ] [ 0 ] + a [ i ] , a [ 0 ] ) f[i][0] = max(f[i-1][0] + a[i], a[0]) 要么接着前一段,要么新开一段。
    • f [ i ] [ 1 ] = m a x ( f [ i 1 ] [ 1 ] + a [ i ] , f [ i 1 ] [ 0 ] ) f[i][1] = max(f[i-1][1] + a[i], f[i-1][0]) 要么接着前一段;要么删除 a [ i ] a[i] 就此断开。
  • 思考输出 m a x ( f [ 0... n ) [ 0 ] , f [ 0... n ) [ 1 ] ) max(f[0...n)[0], f[0...n)[1])
class Solution {
    public int maximumSum(int[] a) {
        int n = a.length, INF = (int) -2e5, max = a[0], f[][] = new int[n][2];
        f[0][0] = a[0];
        f[0][1] = 0;
        
        for (int i = 1; i < n; i++) {
            f[i][0] = Math.max(a[i], f[i-1][0] + a[i]);
            f[i][1] = Math.max(f[i-1][0], f[i-1][1] + a[i]);
            max = Math.max(max, Math.max(f[i][0], f[i][1]));
        }
        return max;
    }
}

复杂度分析

  • 时间复杂度: O ( ) O()
  • 空间复杂度: O ( ) O()

方法二:空间压缩

可以进行空间压缩,因为 f[i][0] 和 f[i][1] 只与前前一个状态有关

class Solution {
    public int maximumSum(int[] a) {
        int n = a.length, max = a[0];
        int fi_0 = a[0], fi_1 = 0;

        for (int i = 1; i < n; i++) {
            int pre_fi_0 = fi_0;
            fi_0 = Math.max(fi_0 + a[i], a[i]);
            fi_1 = Math.max(fi_1 + a[i], pre_fi_0);
            max = Math.max(max, Math.max(fi_0, fi_1));
        }
        return max;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n)
  • 空间复杂度: O ( 1 ) O(1)

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/106895770
今日推荐