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 ){
                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(vectorint>>& pairs) {
        sort(pairs.begin(),pairs.end());
        int n = pairs.size();
        vector<int> dp(n,1);
        for(int i = 0; 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 ){
            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(vectorint>>& 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());
    }
};

 

你可能感兴趣的:(dp-最长子序列问题)