一个下标从 0 开始的数组的 交替和 定义为 偶数 下标处元素之 和 减去 奇数 下标处元素之 和 。
比方说,数组 [4,2,5,3] 的交替和为 (4 + 5) - (2 + 3) = 4 。
给你一个数组 nums ,请你返回 nums 中任意子序列的 最大交替和 (子序列的下标 重新 从 0 开始编号)。
一个数组的 子序列 是从原数组中删除一些元素后(也可能一个也不删除)剩余元素不改变顺序组成的数组。比方说,[2,7,4] 是 [4,2,3,7,2,1,4] 的一个子序列(加粗元素),但是 [2,4,2] 不是。
示例 1:
输入:nums = [4,2,5,3]
输出:7
解释:最优子序列为 [4,2,5] ,交替和为 (4 + 5) - 2 = 7 。
代码
java
class Solution {
public long maxAlternatingSum(int[] nums) {
long[][] dp = new long[nums.length][2];
dp[0][0] = nums[0];
dp[0][1] = Long.MIN_VALUE;
for(int i=1;i<nums.length;i++){
dp[i][0] = Math.max(dp[i-1][1]+nums[i], Math.max(nums[i],dp[i-1][0]));
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-nums[i]);
}
return Math.max(0,Math.max(dp[nums.length-1][0],dp[nums.length-1][1]));
}
}
状态定义
我们定义 dp[i][j]:表示从前 i 个数中选择一个子序列,能得到的最大交替和。
其中 j 只有两个取值(0 或 1),代表子序列末尾元素的特性:
dp[i][0]:子序列的长度为奇数 。- 这意味着最后一个元素的下标是 偶数(0, 2, 4...)。
- 根据题目要求,这个元素是用来 "加" 的。
dp[i][1]:子序列的长度为偶数 。- 这意味着最后一个元素的下标是 奇数(1, 3, 5...)。
- 根据题目要求,这个元素是用来 "减" 的。
状态转移方程
对于第 i 个数 nums[i],我们永远有两个选择:选它 或者 不选它。
(1) 更新 dp[i][0](末尾是加号的状态)
- 如果不选
nums[i]:最大和维持前一个状态,即dp[i-1][0]。 - 如果选
nums[i]:- 它可以接在一个"减号状态"后面:
dp[i-1][1] + nums[i]。 - 它也可以作为子序列的第一个元素 (下标为 0):
nums[i]。
- 它可以接在一个"减号状态"后面:
- 结论 :
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + nums[i], nums[i])。
(2) 更新 dp[i][1](末尾是减号的状态)
- 如果不选
nums[i]:最大和维持前一个状态,即dp[i-1][1]。 - 如果选
nums[i]:- 它必须接在一个"加号状态"后面:
dp[i-1][0] - nums[i]。
- 它必须接在一个"加号状态"后面:
- 结论 :
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - nums[i])。
初始化
dp[0][0] = nums[0];
dp[0][1] = Long.MIN_VALUE;
为什么dp[0][0]为nums[0]?万一nums[0]为负数呢,为什么dp[0][0]不初始化为Math.max(0,nums[0])?
这里的关键在于dp数组的含义,我们定义dp数组为至少要选中一个数,因此dp[0][0]=nums[0],而dp[0][1]不合法。至于一个数都不选的情况,直接放到return处判断,一个数不选就是0,不需要dp专门记录状态。