DP 处理LIS问题

673. Number of Longest Increasing Subsequence

300. Longest Increasing Subsequence

334. Increasing Triplet Subsequence

这三道题目基本一样。

Leetcode 300 题目是最基本的DP算法。

Leetcode 673是300题的延伸。

Leetcode  334 也是300的延伸。

首先第三百题, 给了一个数组,数值任意。给出最长的增长子序列。

当然最先想到的是DFS,把所有的子序列都找到。然后看是不是增长的。

第二个想法就是DP。题目给了两个条件: 有序: LSI是递增。 第二个,求的是最长的LSI值。

既然用了DP,就得:

1. DP的状态: 表示的是到当前位置的数,其最大的LSI长度。

2. 初始化: DP[0]就是第一个元素,到他为止的LSI长度为1.他自己。

3. DP转换方程

如果当前数没有办法拼接到之前的LSI子序列,那么其长度就是1.他自己。

        a) 当前数比之前的小, 就没有办法拼成一个LSI子序列了。

如果当前数可以拼接到之前的LSI子序列,那么其长度是DP[j]+1

         a). 从当前位置往前找,找到数值比i 小的。那么就能和比i小的这个数拼成一个LSI

4. 求结果。

        在DP数组里找最大的那个数。

于是代码就

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {       
        int n = nums.size();
        
        vector<int> dp(n, 1);
        // dp 代表到当前index位置的 最大LIS
        
        dp[0] = 1;
        
        for(int i = 1; i< n; i++){
                //不管当前index的数是大于还是小于index-1的数。都得往前找小于当前数的LIS
                // 比如 0, 1, 0, 3, 2, 3
                //当遍历到2的时候,必须往前遍历到数值为1的点,因为0, 1, 再加上2 也是一个LIS
                // 所以就找之前的比当前点小的num, 然后尝试拼接程LIS。并赋值dp为最长的那个LIS。
                for(int j = i-1;j>=0;j--){
                    if(nums[i] > nums[j]){
                        dp[i] = max(dp[i], dp[j]+1);
                    }
                }
        }
        
        int ret=dp[0];
        
        for(int i = 1;i<n;i++)
            ret = max(ret, dp[i]);
        
        return ret;
    }
};’

673题也是根据300题变化来的。既然现在最长的LSI都求出来了。

求LSI的个数。

看完题目就觉得不会这么简单把。 LSI的个数不就是DP数组里最大的那个数的个数吗?

第一次提交完傻眼了。number of longest increasing subsequences。

这个求的是最长的增长序列的个数。也就是path的个数。

比如DP数组里7 出现了3次。人家不光要求3,还要求每次最长LSI为3 的序列个数。

这样的话就得再多加个数据,去存储到当前节点位置,最长的LSI所对应的路径个数。

那么怎么求那?

关键在这,DP每次更新的时候。

DP公式的想法是把num[i] 和之前的每个数据num[j]对比。

如果num[i]比num[j] 大,说明可以和 num[j]一起拼接程一个LSI。 那么我们就可以根据num[j]的路径数目,算出来到num[]的路径数目。

当然, 也许有多个i可以拼出来一样长度的路径。那么就得找到所有的长度为x的节点的路径。

                for(int j = i-1;j>=0;j--){
                    if(nums[i] > nums[j]){
                        dp[i] = max(dp[i], dp[j]+1);
                    }
                }

把之前的代码改一些。

        //递推方程是往前寻找能否拼成increasing的子序列。
        for(int i=1;i<n;i++){
            int count;
            for(int j = i-1;j>=0;j--){
                if(nums[i]> nums[j]){
                    if(dp[j]+1 > dp[i]){
                        count=numPath[j];
                        dp[i] = dp[j] + 1;
                    }
                    else if (dp[i] == dp[j]+1)
                        count+=numPath[j];
                }
                numPath[i] = count;

            }

到现在为止就能算出来想要的结果了。

代码如下

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        // this question is based on the no. of
        // longest increasing subsequence.
        
        // based on the result of LIS, to find the max
        //LIS and how many it have.
        //本以为不会很难,没想到写起来却无比的艰难。原因是思考的不全面。
        // DP的结果里可能会有多个数值是最大的。本以为把他们的个数加起来就是结果了。但是却没有想到每个最大的个数都可能对应多个路径。比如最长路径是3的点有2个。但是这两个点对应的路径数目可能有多个。
        // 所以在计算每个点对应的DP数值的时候,还要计算对应的最大LIS所对应的路径数目。
        // 最后遍历得到最大的LSI 数值的时候,就要同时遍历最大的LSI的路径数目。并把他们加起来。
        
        
        int n = nums.size();
        vector<int> dp(n, 1);
        vector<int> numPath(n, 1);

        //递推方程是往前寻找能否拼成increasing的子序列。
        for(int i=1;i<n;i++){
            int count = 1;
            for(int j = i-1;j>=0;j--){
                if(nums[i]> nums[j]){
                    if(dp[j]+1 > dp[i]){
                        count=numPath[j];
                        dp[i] = dp[j] + 1;
                    }
                    else if (dp[i] == dp[j]+1)
                        count+=numPath[j];
                }
                numPath[i] = count;

            }
        }
        
        int intMax=0;
        int ret=0;
        
        for(int j = n-1; j>=0;j--){
            if(dp[j] > intMax)
            {
                intMax = dp[j];
                ret =numPath[j];
            }
            else if(dp[j] == intMax){
                ret +=numPath[j];
            }
        }
        
        return ret;
    }
};

334. Increasing Triplet Subsequence

求是否存在LSI长度为3的。

我们大于3的都能求出来。等于3的求不出来了吗?

答案是能求出来,但是超时了。。。。。。。。

代码如下:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int n = nums.size();
        
        vector<int> dp(n, 1);
        
        for(int i= 1;i<n;i++){
            for(int j = i-1; j>=0;j--){
                if(nums[i] > nums[j])
                    dp[i] = max(dp[i], dp[j] + 1); 
            }
            if(dp[i] >=3)
                return true;
        }
        
        return false;
    }
};

看起来得优化了。。。还没有想好咋优化。先写道这把!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值