dp-最长子序列问题
基本思想:
300. 最长上升子序列 M
给定一个无序的整数数组,找到其中最长上升子序列的长度。
通用方法:
class Solution { public: int lengthOfLIS(vector<int>& nums) { int n = nums.size(); if (n == 0) return 0; vector<int> dp(n,1); for(int i = 0; i < n; i++){ for(int j = 0; j < i; j++) if(nums[i] > nums[j]) dp[i] = max(dp[i],dp[j]+1); } return *max_element(dp.begin(),dp.end()); } };
用二分查找,可以将复杂度下降到 NlogN
定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素。对于一个元素 x,
- 如果它大于 tails 数组所有的值,那么把它添加到 tails 后面,表示最长递增子序列长度加 1;
- 如果 tails[i-1] < x <= tails[i],那么更新 tails[i] = x。
class Solution { public: int lengthOfLIS(vector<int>& nums) { int n = nums.size(); if (n == 0) return 0; vector<int> tail(n); int len = 0; for(int i = 0; i < n; i++){ int l = 0,r = len; while(l < r){ int m = l + (r-l)/2;
// nums[i]插入左边 if(tail[m] >= nums[i]) r = m;
// nums[i]插入右边 else l = m+1; } tail[l] = nums[i];
//如果大于所有的数 if(l == len) len++; } return len; } };
673. 最长递增子序列的个数
给定一个未排序的整数数组,找到最长递增子序列的个数。
在上一题的基础上增加一个记录数组:
class Solution { public: int findNumberOfLIS(vector<int>& nums) { int n = nums.size(); if(n == 0) return 0; vector<int> dp(n,1),p(n,1);//注意初始化为1 for(int i = 0; i < n; i++) for(int j = 0; j <i; j++){ if(nums[i] > nums[j])//判断条件 if(dp[i] < dp[j]+1){ dp[i] = dp[j]+1; p[i] = p[j]; } else if(dp[i] == dp[j]+1)//相等等于两个支路相加 p[i]+=p[j]; } int mm = *max_element(dp.begin(),dp.end()); int res = 0; for(int i = 0; i < n; i++) if(dp[i] == mm) res+= p[i]; return res; } };
646. 最长数对链
给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。
现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。
给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
通用方法:
class Solution { public: int findLongestChain(vector<vector<int>>& pairs) { sort(pairs.begin(),pairs.end()); int n = pairs.size(); vector<int> dp(n,1); for(int i = 0; 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); return *max_element(dp.begin(),dp.end()); } };
376. 摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
通用方法:
class Solution { public: int wiggleMaxLength(vector<int>& nums) { int n = nums.size(); if(n == 0) return 0; if( n <= 2) return 1; vector<int> up(n,1),down(n,1); for(int i = 1; i <n; i++){ for(int j = 0;j < i; j++) if(nums[i] > nums[j]) up[i] = max(up[i],down[j]+1); else if(nums[i] < nums[j]) down[i] = max(down[i],up[j]+1); } return max(up[n-1],down[n-1]); } };
354. 俄罗斯套娃信封问题
给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
通用方法:
class Solution { public: int maxEnvelopes(vector<vector<int>>& envelopes) { int n = envelopes.size(); if( n== 0) return 0; sort(envelopes.begin(),envelopes.end()); vector<int> dp(n,1); for(int i = 0; i < n; i++) for(int j = 0; j < i; j++){ if(envelopes[i][0] > envelopes[j][0] && envelopes[i][1] > envelopes[j][1]) dp[i] = max(dp[i],dp[j]+1); } return *max_element(dp.begin(),dp.end()); } };