本题的核心在于对于每个元素,我们分别考虑保留和删除两种状态,并根据前面的状态转移来更新当前状态。最后,遍历所有元素,找到最大和即可。
状态定义
dp[i][0]
表示以第i
个元素结尾且未删除元素的子数组的最大和。dp[i][1]
表示以第i
个元素结尾且删除了一个元素后的子数组的最大和。
状态转移方程
dp[i][0]
这是比较容易理解的部分。对于 dp[i][0]
,我们只需要考虑当前元素是否能使前面的子数组和变得更大:
dp[i][0]=max(dp[i−1][0],0)+arr[i]
这里的 max(dp[i-1][0], 0)
意味着:
-
如果
dp[i-1][0]
是正的,加上当前元素arr[i]
可以让子数组和变大。 -
如果
dp[i-1][0]
是负的,我们不需要前面的子数组,直接从arr[i]
开始一个新的子数组。
dp[i][1]
对于 dp[i][1]
,我们需要考虑是否删除一个元素,具体分为以下两种情况:
-
arr[i]
是正的。 -
arr[i]
是负的。
arr[i]
是正的:
如果当前元素是正的,我们可以选择将它加到之前的子数组上,因为这样肯定能使子数组和变大。这里需要考虑的是:
-
前面的子数组是否已经删除过一个元素 (
dp[i-1][1]
),如果是且dp[i-1][1]
是正的,则可以加上当前元素。 -
如果
dp[i-1][1]
不是正的,我们就可以看作从i-1
开始新起一个子数组(即第i-1
个元素是被删除的那个)。
公式为:dp[i][ 1]=max(dp[i−1][1]+arr[i],dp[i−1][0])
arr[i]
是负的:
对于负数,我们有两种选择:
-
删除当前的负数
arr[i]
,接上前面的子数组,这样最大和就是dp[i-1][0]
。 -
不删除当前的负数,而是把它加到已经删除过一个元素的子数组上(即
dp[i-1][1]
),这时最大和是dp[i-1][1] + arr[i]
。
公式为: ·dp[i][1]=max(dp[i−1][0],dp[i−1][1]+arr[i])
代码实现
public class Solution {
public int maximumSum(int[] arr) {
int n = arr.length;
int[][] dp = new int[n][2];
dp[0][0] = arr[0];
dp[0][1] = 0; // 刚开始没有删除元素,删除后至少应保留一个元素
int maxSum = arr[0];
for (int i = 1; i < n; i++) {
// 状态转移方程
dp[i][0] = Math.max(dp[i - 1][0], 0) + arr[i];
dp[i][1] = Math.max(dp[i - 1][1] + arr[i], dp[i - 1][0]);
// 更新最大和
maxSum = Math.max(maxSum, Math.max(dp[i][0], dp[i][1]));
}
return maxSum;
}
public static void main(String[] args) {
Solution solution = new Solution();
int[] arr1 = {1, -2, 0, 3};
int[] arr2 = {1, -2, -2, 3};
int[] arr3 = {-1, -1, -1, -1};
System.out.println(solution.maximumSum(arr1)); // 4
System.out.println(solution.maximumSum(arr2)); // 3
System.out.println(solution.maximumSum(arr3)); // -1
}
}