leetcode:1186. 删除一次得到子数组最大和

题目来源

题目描述

在这里插入图片描述

class Solution {
public:
    int maximumSum(vector<int>& arr) {

    }
};

题目解析

状态机DP

什么是状态机呢?

比如进程转换就是一个状态机

在这里插入图片描述

而状态机DP,简单的说,dp[i]一个值不够用了,需要多几个了。每个值就相当于上图中的一个圆圈,每个状态之间可以相互转换

典型的状态机DP问题

  • 股票问题:由于股票存在多次买入卖出,也就是dp[i]存在多种可能(还没卖、还没买、处于冷冻期不能买等)
  • 最大连续乘积问题:和最大子数组和相比,乘法会出现负数,此时最小边最大,最大变最小。因此dp[i]存在最大值和最小值两个状态。leetcode题号是153题
  • 摆动序列问题:有时摆动序列以下降结尾,下一个值应该是上升;有时则相反。向前找dp[i]时,最大最小都得用到,所以存两个值。376 能够形成摆动序列的最长子序列长度

状态DP问题的解法

(1)找到k个状态

  • 股票问题是最典型的状态机DP问题。以此为例我们考虑,遍历到i时,当天卖出,当天不卖,有冷冻期不能卖、无冷冻期不能卖等。这些中什么是状态,需要我们新增一个变量去控制?
    • “当天卖出”不属于状态,属于动作。动作用于连接状态,但不占用dp的状态位
    • “当天不持有股票”是静态描述,属于状态
    • “当天出于冷冻期”是静态描述,属于状态
  • 由此,以动静区分。简单题非常容易看清楚几个状态,但难题很容易混淆。状态没找好,后面就全乱了。

(2)初始化k个变量

  • 初始化为第0个数执行各个动作之后的状态即可。后面从nums[i]开始遍历

(3)依据状态之间的关系逐个更新

  • 具体问题具体分析即可

对于本题

(1)定义

  • dp[i][0]:不删除元素,以i结尾的连续子数组最大和
  • dp[i][1]:删除其中一个元素得到的最大值,有两种情况:
    • 删除i
    • 不删除i

(2)思路

  • dp[i + 1][ 0]有两种情况:
    • 如果dp[i][0]大于0,那么dp[i + 1][0] = dp[i][0] + arr[i]
    • 如果dp[i][0]小于0,那么dp[i + 1][0] = arr[i]
  • dp[i+1][1]的计算有两种情况:
    • 删除位置i的数:dp[i+1][1]=dp[i][0]
    • 删除其他位置的数:dp[i+1][1]=dp[i][1]+arr[i]
  • ans存储两者的较大值
class Solution{
public:
    int maxAbsoluteSum(vector<int>& arr) {
        int n = arr.size(), ret = arr[0];
        //dp[i][0]表示不删除,dp[i][1]表示删除子序列的某一个元素(仅一个)
        vector<vector<int>> dp(n, vector<int>(2, 0));
        dp[0][0] = arr[0];//dp[0][1]=0,表示删除第一个元素,不能拿来作为结果值
        for (int i = 1; i < n; ++i) {
            //不删除时,就是求子数组最大和
            dp[i][0] = std::max(0, dp[i - 1][0]) + arr[i];
            if(arr[i] >= 0){
                dp[i][1] = dp[i - 1][1] + arr[i]; //在大于等于0时,当前元素肯定是不删除的
            }else{
                //在小于0时,删除当前(dp1[i-1])和当前不删的最大值dp2[i-1]+arr[i]
                dp[i][1] = max(dp[i - 1][0], dp[i - 1][1] + arr[i]);
            }
            ret = std::max(ret, std::max(dp[i][0], dp[i][1]));
        }
        return ret;
    }
};
class Solution {
    struct Info{
        int allIn;     // 不删除元素,以i结尾的连续子数组最大和
        int deleteOne; // 删除其中一个元素得到的最大值(不一定以i结尾, 无论如何删除一次)

        Info(){}

        Info(int allIn, int deleteOne) : allIn(allIn), deleteOne(deleteOne){

        }
    };
public:
    // 1 <= arr.length <= 10^5
    int maximumSum(vector<int>& arr) {
        int n = arr.size();
        std::vector<Info> dp(n);  // [0...i]
        dp[0].allIn = arr[0];
        dp[0].deleteOne = 0;
        int res = dp[0].allIn;  //   dp[0].deleteOne不是有效答案--->子数组 不能为空。
        for (int i = 1; i < n; ++i) {
            //不删除时,就是求子数组最大和
            dp[i].allIn = std::max(0, dp[i - 1].allIn) + arr[i];
            dp[i].deleteOne = std::max(dp[i - 1].allIn, dp[i - 1].deleteOne + arr[i]);
            res = std::max(res, std::max(dp[i].allIn, dp[i].deleteOne));
        }
        return res;
    }
};

类似题目

题目思路
leetcode:53. 子数组最大累加和 maximum-subarray
leetcode:1749. 任意子数组和的绝对值的最大值 Maximum Absolute Sum of Any Subarray
leetcode:1186. 删除一次得到子数组最大和 maximum-subarray-sum-with-one-deletion
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值