新增376.摆动序列 动态规划解法

This commit is contained in:
0x404
2021-10-01 13:51:39 +08:00
parent 33768a0ffb
commit 3c3f8a883e

View File

@ -33,8 +33,7 @@
输入: [1,2,3,4,5,6,7,8,9] 输入: [1,2,3,4,5,6,7,8,9]
输出: 2 输出: 2
## 思路1贪心解法
## 思路
本题要求通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。 本题要求通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
@ -93,6 +92,69 @@ public:
时间复杂度O(n) 时间复杂度O(n)
空间复杂度O(1) 空间复杂度O(1)
## 思路2动态规划
考虑用动态规划的思想来解决这个问题。
很容易可以发现对于我们当前考虑的这个数要么是作为山峰即nums[i] > nums[i-1]要么是作为山谷即nums[i] < nums[i - 1])。
* 设dp状态`dp[i][0]`表示考虑前i个数第i个数作为山峰的摆动子序列的最长长度
* 设dp状态`dp[i][1]`表示考虑前i个数第i个数作为山谷的摆动子序列的最长长度
则转移方程为
* `dp[i][0] = max(dp[i][0], dp[j][1] + 1)`其中`0 < j < i``nums[j] < nums[i]`表示将nums[i]接到前面某个山谷后面作为山峰
* `dp[i][1] = max(dp[i][1], dp[j][0] + 1)`其中`0 < j < i``nums[j] > nums[i]`表示将nums[i]接到前面某个山峰后面作为山谷
初始状态
由于一个数可以接到前面的某个数后面也可以以自身为子序列的起点所以初始状态为`dp[0][0] = dp[0][1] = 1`
C++代码如下
```c++
class Solution {
public:
int dp[1005][2];
int wiggleMaxLength(vector<int>& nums) {
memset(dp, 0, sizeof dp);
dp[0][0] = dp[0][1] = 1;
for (int i = 1; i < nums.size(); ++i)
{
dp[i][0] = dp[i][1] = 1;
for (int j = 0; j < i; ++j)
{
if (nums[j] > nums[i]) dp[i][1] = max(dp[i][1], dp[j][0] + 1);
}
for (int j = 0; j < i; ++j)
{
if (nums[j] < nums[i]) dp[i][0] = max(dp[i][0], dp[j][1] + 1);
}
}
return max(dp[nums.size() - 1][0], dp[nums.size() - 1][1]);
}
};
```
时间复杂度O(n^2)
空间复杂度O(n)
**进阶**
可以用两棵线段树来维护区间的最大值
* 每次更新`dp[i][0]`,则在`tree1`的`nums[i]`位置值更新为`dp[i][0]`
* 每次更新`dp[i][1]`,则在`tree2`的`nums[i]`位置值更新为`dp[i][1]`
* 则dp转移方程中就没有必要j从0遍历到i-1可以直接在线段树中查询指定区间的值即可。
时间复杂度O(nlogn)
空间复杂度O(n)
## 总结 ## 总结
**贪心的题目说简单有的时候就是常识,说难就难在都不知道该怎么用贪心**。 **贪心的题目说简单有的时候就是常识,说难就难在都不知道该怎么用贪心**。