最長上升子序列【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();
    }
};

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