Medium

题目描述

给你一个整数数组,返回它的某个 非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。换句话说,你可以从原数组中选择一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。

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

示例 1:

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

示例 2:

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

示例 3:

输入:arr = [-1,-1,-1,-1]
输出:-1
解释:最后得到的子数组不能为空。你不能选择 [-1] 然后删掉 -1 来得到 0。应该直接选择 [-1],所以最大和是 -1。

提示:

  • 1 <= arr.length <= 10^5
  • -10^4 <= arr[i] <= 10^4

解题思路

这道题是经典的 Kadane 算法的变种,需要考虑最多删除一个元素的情况。

核心思路: 我们需要维护两个动态规划状态:

  1. dp0[i]:以位置 i 结尾,没有删除任何元素的最大子数组和
  2. dp1[i]:以位置 i 结尾,删除了一个元素的最大子数组和

状态转移:

  • dp0[i] = max(arr[i], dp0[i-1] + arr[i])(标准 Kadane 算法)
  • dp1[i] 有两种情况:
    • 删除当前元素:dp0[i-1]
    • 不删除当前元素:dp1[i-1] + arr[i]
    • 取两者最大值

优化空间: 由于只需要前一个状态,可以用两个变量代替数组,将空间复杂度优化到 O(1)。

注意点:

  • 初始化时,dp1[0] 应该设为负无穷,因为第一个元素不能被删除(否则子数组为空)
  • 答案是所有 dp0[i]dp1[i] 中的最大值

代码实现

class Solution {
public:
    int maximumSum(vector<int>& arr) {
        int n = arr.size();
        int dp0 = arr[0];  // 没有删除元素的最大和
        int dp1 = 0;       // 删除一个元素的最大和
        int result = arr[0];
        
        for (int i = 1; i < n; i++) {
            // 删除一个元素的情况:要么删除当前元素,要么在之前删除的基础上加上当前元素
            dp1 = max(dp0, dp1 + arr[i]);
            // 没有删除元素的情况:标准Kadane算法
            dp0 = max(arr[i], dp0 + arr[i]);
            // 更新结果
            result = max(result, max(dp0, dp1));
        }
        
        return result;
    }
};
class Solution:
    def maximumSum(self, arr: List[int]) -> int:
        n = len(arr)
        dp0 = arr[0]  # 没有删除元素的最大和
        dp1 = 0       # 删除一个元素的最大和
        result = arr[0]
        
        for i in range(1, n):
            # 删除一个元素的情况:要么删除当前元素,要么在之前删除的基础上加上当前元素
            dp1 = max(dp0, dp1 + arr[i])
            # 没有删除元素的情况:标准Kadane算法
            dp0 = max(arr[i], dp0 + arr[i])
            # 更新结果
            result = max(result, dp0, dp1)
        
        return result
public class Solution {
    public int MaximumSum(int[] arr) {
        int n = arr.Length;
        int dp0 = arr[0];  // 没有删除元素的最大和
        int dp1 = 0;       // 删除一个元素的最大和
        int result = arr[0];
        
        for (int i = 1; i < n; i++) {
            // 删除一个元素的情况:要么删除当前元素,要么在之前删除的基础上加上当前元素
            dp1 = Math.Max(dp0, dp1 + arr[i]);
            // 没有删除元素的情况:标准Kadane算法
            dp0 = Math.Max(arr[i], dp0 + arr[i]);
            // 更新结果
            result = Math.Max(result, Math.Max(dp0, dp1));
        }
        
        return result;
    }
}
var maximumSum = function(arr) {
    const n = arr.length;
    let dp0 = arr[0];  // 没有删除元素的最大和
    let dp1 = 0;       // 删除一个元素的最大和
    let result = arr[0];
    
    for (let i = 1; i < n; i++) {
        // 删除一个元素的情况:要么删除当前元素,要么在之前删除的基础上加上当前元素
        dp1 = Math.max(dp0, dp1 + arr[i]);
        // 没有删除元素的情况:标准Kadane算法
        dp0 = Math.max(arr[i], dp0 + arr[i]);
        // 更新结果
        result = Math.max(result, dp0, dp1);
    }
    
    return result;
};

复杂度分析

复杂度数值
时间复杂度O(n)
空间复杂度O(1)

相关题目