题目来源
题目描述
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 |