删除一次得到子数组最大和

删除一次得到子数组最大和

题目来源 LeetCode link

一、题目

Description

​ 给你一个整数数组,返回它的某个 非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。
​ 也就是说,从一个数组中找出一个连续的子数组,你可以从这个子数组中选择是否删除一个元素,得到最大的子数组和。

注意
可以选择删除一个元素,也可以不删除。
删除一个元素后,子数组不能为空。

Input

输入的第一行是用例个数T,第二行是数组长度n,第三行是数组,数组元素用空格分割。

Output

输出最大子数组的和。

提示:

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

Sample Input 1
2
4
1 -2 0 3
4
-2 -1 -3 -4
Sample Output 1
4
-1

二、解题思路

使用动态规划思想。
  1. 构建二维数组 dp[2][n]

    本题要求最多删除一个元素,则该数组构建为2行,n列。

    第0行 dp[0][j] 表示前j个元素中不删除的最大子数组的和;

    第1行 dp[1][j] 表示前j个元素中删除一个的最大子数组的和;

  2. dp[i][j] 初始化

    dp[0][0] = arr[0];

    1. 初始化第0行 (前j个元素不删除的最大子数组和)

      dp[0][j] = Math.max(0, dp[0][j - 1]) + arr[j];    //j从1开始
      

      dp[0][j] 有两种情况:

      ① 前j-1个元素的和小于0时,加上arr[j], 就小于arr[j],此时就以arr[j]开始重新累加,即dp[0][j]=0+arr[j];

      ② 前j-1个元素的和大于0时,就把arr[j]加上,即dp[0][j]=dp[0][j-1]+arr[j];

      取以上两者中的最大值。

    2. 初始化第1行 (前j个元素删除一个元素的最大子数组和)

      dp[1][j] = Math.max(dp[1][j - 1] + arr[j], dp[0][j - 1]);		//j从1开始
      

      对于第j个元素,有两种情况:

      ① 从前j-1个元素中删除一个,在加上arr[j],也就是前j个元素删除一个了一个,即dp[1][j]=dp[1][j-1]+arr[j];

      ② 删除第j个元素,值为前j-1个元素不删除的最大子数组和,即dp[1][j]=dp[0][j-1];

      从以上两者中选最大值。

      注意:dp[1][0]不考虑,默认为0就可以,因为子数组不能为空。

      dp[1][1] = Math.max(dp[1][0] + arr[1], dp[0][0]);就是在arr[0]到arr[1]中删除一个的最大值;

三、代码

java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int numOfTestcase = scanner.nextInt();
        while (numOfTestcase-- > 0) {
            int n = scanner.nextInt();
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = scanner.nextInt();
            }
            System.out.println(maximumSum(arr));
        }
    }

    public static int maximumSum(int[] arr) {
        int n = arr.length;
        
        //数组有2行,第0行为不删除元素的最大子数组和,第1行为删除一个元素的最大子数组和
        //dp[1][3]为前4个元素中删除一个元素的最大子数组和
        int[][] dp = new int[2][n];

        //初始化
        int max = arr[0];		//max为不删除和删除一个元素的子数组和中的最大值
        //初始化 dp[0][0],子数组不能为空,最少为一个
        dp[0][0]  = arr[0];
        for (int j = 1; j < n; j++) {
            //dp[0][j] 不删除元素的最大子数组和
            dp[0][j] = Math.max(0, dp[0][j - 1]) + arr[j];
            max = Math.max(max, dp[0][j]);
            
            //dp[0][j] 删除一个元素的最大子数组和
            dp[1][j] = Math.max(dp[1][j - 1] + arr[j], dp[0][j - 1]);
            max = Math.max(max, dp[1][j]);
        }
        return max;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
解题思路: 题目要求我们寻找一个最短的数组,使得删去它之后原数组变成非递减数组。那么我们可以考虑贪心策略。首先,我们需要找到最长的非递减数组,这个数组在最后的答案中肯定是不会被删除的,因为删去这个数组回导致整个数组非递减性质被破坏。接着我们假设这个数组的右端点为 i(当然我们还不知道实际的值是多少),那么我们接着考虑如何从左边找到最短的数组,使得删去这个数组之后原数组变为非递减数组。假设我们找到了左端点为 j 的数组,那么这个数组的长度就是 i-j-1。然后我们可以用一个变量 cnt 来统计需要从这个数组中删去的数字的个数,这个变量的初始化值为 0,每当我们找到一个数组的边界,而这个数组又不是非递减的时候,我们就将这个数组中从小到大排列的数字删掉并且让 cnt++。最终答案就是 i-j-1-cnt,即最长的非递减数组长度减去从左边找到的最短需要删除数组的长度再减去被删除的数字个数。 具体实现可以使用单调栈,单调栈里存的是数组元素的下标,栈顶到栈底位置对应的元素值是单调不降的。遍历数组的时候,如果当前元素比单调栈的栈顶小,说明需要找到右端点,于是不断pop出深度直到栈顶元素小于当前元素或者栈空,此时当前元素的下标就是右端点。接着我们在单调栈里寻找左端点,这里需要注意我们需要从右往左遍历单调栈,因为要保证最先找到的左端点一定是在右端点左边的。用一个变量 cnt 统计需要删除的元素个数,最终计算答案得到结果。 时间复杂度:O(n),两次单调栈遍历,最差情况下遍历整个数组。空间复杂度:O(n),单调栈中的元素个数最大为 n。 代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值