赞
踩
题目链接:https://leetcode.cn/problems/longest-increasing-subsequence/
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
提示:
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
思路
1. 状态表达: 我们定义一个动态规划数组 dp
,其中 dp[i]
表示以第 i
个位置元素为结尾的所有「子序列」中,最长递增子序列的长度。
2. 状态转移方程: 针对 dp[i]
的状态,我们考虑「子序列的构成方式」进行分类讨论:
1
时,只包含自身,因此 dp[i] = 1
;1
时,我们遍历前面的所有元素 j
(0 <= j <= i - 1
),检查是否可以将第 i
个位置的元素接在第 j
个元素之后形成递增子序列。如果 nums[j] < nums[i]
,说明可以构成递增序列,更新状态:dp[i] = max(dp[j] + 1, dp[i])
。3. 初始化: 初始时,每个元素都可以构成长度为 1
的递增子序列,因此将整个 dp
数组初始化为 1
。
4. 填表顺序: 我们按照从左到右的顺序遍历数组。
5. 返回值: 由于不知道最长递增子序列以哪个元素结尾,我们返回 dp
数组中的最大值。这样的动态规划算法通过遍历数组,不断更新状态,最终得到以每个位置为结尾的子序列中最长递增子序列的长度。最后返回数组中所有元素的最大值。
代码
class Solution { public: int lengthOfLIS(vector<int>& nums) { int n=nums.size(); vector<int> dp(n,1); int ret=1; for(int i=1;i<n;i++){ for(int j=0;j<i;j++){ if(nums[j]<nums[i]) dp[i]=max(dp[i],dp[j]+1); } ret=max(ret,dp[i]); } return ret; } };
题目链接:https://leetcode.cn/problems/wiggle-subsequence/
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 **摆动序列 。**第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
[1, 7, 4, 9, 2, 5]
是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3)
是正负交替出现的。[1, 4, 7, 2, 5]
和 [1, 7, 4, 5, 5]
不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums
,返回 nums
中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。
示例 2:
输入:nums = [1,17,5,10,13,15,10,5,16,8]
输出:7
解释:这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。
示例 3:
输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2
提示:
1 <= nums.length <= 1000
0 <= nums[i] <= 1000
思路
1. 状态表达: 我们定义两个动态规划数组 f
和 g
,其中:
f[i]
表示以第 i
个位置元素为结尾的所有的子序列中,最后一个位置呈现「上升趋势」的最长摆动序列的长度。g[i]
表示以第 i
个位置元素为结尾的所有的子序列中,最后一个位置呈现「下降趋势」的最长摆动序列的长度。2. 状态转移方程: 对于 f[i]
的状态,我们考虑子序列的构成方式:
1
时,只包含自身,因此 f[i] = 1
。1
时,遍历前面的所有元素 j
(0 <= j <= i - 1
),检查是否可以将第 i
个位置的元素接在第 j
个元素之后形成递增子序列。如果 nums[j] < nums[i]
,则在满足这个条件下,要使 j
结尾呈现下降状态,最长的摆动序列即为 g[j] + 1
。更新状态:f[i] = max(g[j] + 1, f[i])
。对于 g[i]
的状态,我们同样考虑子序列的构成方式:
1
时,只包含自身,因此 g[i] = 1
。1
时,遍历前面的所有元素 j
(0 <= j <= i - 1
),检查是否可以将第 i
个位置的元素接在第 j
个元素之后形成递减子序列。如果 nums[j] > nums[i]
,则在满足这个条件下,要使 j
结尾呈现上升状态,最长的摆动序列即为 f[j] + 1
。更新状态:g[i] = max(f[j] + 1, g[i])
。3. 初始化: 初始时,每个元素都可以构成长度为 1
的摆动序列,因此将整个 f
和 g
数组初始化为 1
。
4. 填表顺序: 按照从左到右的顺序遍历数组。
5. 返回值: 返回 f
和 g
数组中的最大值。这样的动态规划算法通过遍历数组,不断更新状态,最终得到以每个位置为结尾的子序列中最长的摆动序列长度。最后返回两个数组中的最大值。
代码
class Solution { public: int wiggleMaxLength(vector<int>& nums) { int n=nums.size(); vector<int> f(n,1),g(n,1); int ret=1; for(int i=1;i<n;++i){ for(int j=0;j<i;j++){ if(nums[j]<nums[i]) f[i]=max(g[j]+1,f[i]); else if(nums[j]>nums[i]) g[i]=max(f[j]+1,g[i]); } ret=max(ret,max(g[i],f[i])); } return ret; } };
题目链接:https://leetcode.cn/problems/number-of-longest-increasing-subsequence/
给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。
注意 这个数列必须是 严格 递增的。
示例 1:
输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
提示:
1 <= nums.length <= 2000
-106 <= nums[i] <= 106
思路
1. 状态表达: 我们定义两个动态规划数组 len
和 count
,其中:
len[i]
表示以第 i
个位置为结尾的最长递增子序列的长度。count[i]
表示以第 i
个位置为结尾的最长递增子序列的个数。2. 状态转移方程: 首先,对于 len[i]
的状态,我们考虑子序列的构成方式:
1
时,只包含自身,因此 len[i] = 1
。1
时,遍历前面的所有元素 j
(0 <= j < i
),检查是否可以将第 i
个位置的元素接在第 j
个元素之后形成递增子序列。如果 nums[j] < nums[i]
,则在满足这个条件下,要使 j
结尾呈现上升状态,最长的递增子序列即为 len[j] + 1
。更新状态:len[i] = max(len[j] + 1, len[i])
。接着,对于 count[i]
的状态,我们考虑子序列的构成方式:
len[i]
的信息,还知道 [0, i - 1]
区间上的 count[j]
信息,使用 j
表示 [0, i - 1]
区间上的下标。[0, i - 1]
区间上的所有元素,只要能够构成上升序列,并且上升序列的长度等于 len[i]
,那么我们就把 count[i]
加上 count[j]
的值。这样循环一遍之后,count[i]
存的就是我们想要的值。更新状态:count[i] += count[j]
,其中 0 <= j < i
且 nums[j] < nums[i] && len[j] + 1 == len[i]
。3. 初始化:
len[i]
,所有元素自己就能构成一个递增序列,直接全部初始化为 1
。count[i]
,我们先初始化为 0
,然后在累加的时候判断。4. 填表顺序: 从左往右遍历数组。
5. 返回值: 最终的最长递增子序列的长度为 maxLen
。根据题目要求,返回所有长度等于 maxLen
的子序列的个数。
代码
class Solution { public: int findNumberOfLIS(vector<int>& nums) { int n = nums.size(); vector<int> len(n, 1), count(n, 1); int retlen = 1, retcount = 1; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[j] < nums[i]) { if (len[j] + 1 == len[i]) count[i] += count[j]; else if (len[j] + 1 > len[i]) len[i] = len[j] + 1, count[i] = count[j]; } } if (retlen == len[i]) retcount += count[i]; else if (retlen < len[i]) retlen = len[i], retcount = count[i]; } return retcount; } };
题目链接:https://leetcode.cn/problems/maximum-length-of-pair-chain/
给你一个由 n
个数对组成的数对数组 pairs
,其中 pairs[i] = [lefti, righti]
且 lefti < righti
。
现在,我们定义一种 跟随 关系,当且仅当 b < c
时,数对 p2 = [c, d]
才可以跟在 p1 = [a, b]
后面。我们用这种形式来构造 数对链 。
找出并返回能够形成的 最长数对链的长度 。
你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
示例 1:
输入:pairs = [[1,2], [2,3], [3,4]]
输出:2
解释:最长的数对链是 [1,2] -> [3,4] 。
示例 2:
输入:pairs = [[1,2],[7,8],[4,5]]
输出:3
解释:最长的数对链是 [1,2] -> [4,5] -> [7,8] 。
提示:
n == pairs.length
1 <= n <= 1000
-1000 <= lefti < righti <= 1000
思路
dp
,其中 dp[i]
表示以第 i
个数对为结尾的最长数对链的长度。dp[i]
,遍历所有 [0, i - 1]
区间内的数对,用 j
表示下标。找出所有满足 pairs[j][1] < pairs[i][0]
的 j
,然后找出其中最大的 dp[j]
,最后加上 1
就是以第 i
个数对为结尾的最长数对链。状态转移方程为:dp[i] = max(dp[j] + 1, dp[i])
,其中 0 <= j < i
。1
。dp
数组中的最大值。代码
class Solution { public: int findLongestChain(vector<vector<int>>& pairs) { sort(pairs.begin(),pairs.end()); int n=pairs.size(); vector<int> dp(n,1); int ret=1; for(int i=1;i<n;i++){ for(int j=0;j<i;j++){ if(pairs[i][0]>pairs[j][1]) dp[i]=max(dp[i],dp[j]+1); } ret=max(ret,dp[i]); } return ret; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。