最长上升子序列【leetcode每日一题】【LIS完整优化思路】

在这里插入图片描述
写在前面:说来也巧,昨天刚在洛谷上做线性dp,在学习LIS和LCS的优化问题,今天每日一题就出了这道LIS。

思路:
首先说朴素算法的思路,
首先我们要定义一个集合,我们不妨以dp【i】表示数组中以第i位结尾的最长上升子序列的长度,然后从左往右依次迭代就可以了。
然后我们需要确定状态,dp【i】表示数组中以第i位结尾的最长上升序列长度,那么它和之前dp【0~i】有什么关系呢?如果nums【i】>nums【j】其中j=【0…i】那么dp【i】=dp【j】+1.至此,我们就确定了状态转移方程dp【i】=max(dp【j】+1,dp【i】)其中j是0到i且所有满足条件的下标。
代码如下:

/*class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty())    return 0;
        if(nums.size()==1)  return 1;
        vector<int>dp(nums.size(),0);
        for(int i=0;i<nums.size();i++)  
        {
            dp[i]=1;
            for(int j=0;j<i;j++)
                if(nums[i]>nums[j])
                    dp[i]=max(dp[j]+1,dp[i]);
        }
        return *max_element(dp.begin(), dp.end());
    }
};

上面这个算法很明显,时间复杂度是O(n^2)的。
然后我们考虑如何优化呢?
首先我们从上面那个算法可以知道,我们每次更新值是因为找到了前面可以更新的点,最后枚举找出最大的。
那么我们首先换一个思路,我们不妨定义dp【i】表示长度位i的上升子序列的最小结尾的数。
打个比方,比如第一个数为9,dp数组里面就放了一个【9】,他代表长度为1的子序列的最小结尾值为9,然后第二个数为2,2<9。所以2开始后面可能会有一个上升的序列,9后面也可能有一个上升的序列(有没有感觉像列车调度这道题)那么dp数组就更新为【2】表示当前长度为1的上升序列最小结尾为2.然后第三个数为4,4>2,这个是重点,表示4大于当前长度为1的上升序列的最小结尾,那么把他接到2后面长度就变成了2,所以dp数组里面就是【2,4】表示现在长度为1的上升序列最小结尾为2,长度为2的上升序列的最小结尾为4。如果这个数组里面就是9,2,4这三个数,那么此时程序已经运行完了,就直接输出dp数组的大小(就是2)就ojbk了。(是不是感觉越发的像列车调度这道题了。)

好,这个思路讲完了,我们总结一下,插入新的数的时候,(比如还是上面那个例子,我们紧接着要插入一个3,3比2大比4小所以要替换4的位置,如果是插入1,1比2小所以替换2的位置)。也就是说我们要在dp数组里找最接近插入的数的位置,这么经典的查找算法我们自然而然就想到了二分(因为dp数组里的数是严格单调递增的)。至此,优化完成!
代码如下:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> minnums;
        for(int v : nums)
        {
            if(!minnums.size() || v > minnums.back()) 
                minnums.push_back(v);
            else
                *lower_bound(minnums.begin(), minnums.end(), v) = v;
        }
        return minnums.size();
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章