动态规划——最长上升子序列

问题

    最长上升子序列是一类经典的动态规划问题。

给定N个数字, A1,A2,....An,从中选择k个数字 At1, At2,... Atk,满足 1 =< t1 < t2 < .. < tk <= n,且 At1 < At2 < ... < Atk,求满足要求的最大的k。

分析

    设一个动归数组dp,dp[i]表示以第i个数字(即Ai)结尾的最长上升子序列的长度,显然这种问题的划分满足无后效性和最优子结构。同时,可以很方便的推出递推关系 
dp[i] = max{1, dp[j] +1} (j < i 且 Ai > Aj)

实现

memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i ++){ //数组从1开始
    int max = 0;
    for(int j = 1; j < i; j ++){
        if(A[i] > A[j])
            dp[i] = max(dp[i], dp[j] + 1);
            if (dp[i] > max)
                max = dp[j];
    }
}    

复杂度分析

    显然,时间复杂度为O(n^2), 空间复杂度为O(n).

 

O(nlogn)复杂度解法

其实还存在一种时间复杂度为O(n*logn)的算法,也是使用动态规划思想,状态 dp[p] 为 所有最长长度为p+1的上升子序列中最后一个元素的最小值。

这样,从头到尾遍历数组nums,对于当前的nums[i],在dp中查找第一个比nums[i]大的数 dp[k],如果没找到,则dp数组的长度p增加1,并且设置dp[p] = nums[i],

如果找到,则将 dp[k] 赋值为 nums[i](因为nums[i]是此时最长长度为k+1的上升子序列中最后一个元素的最小值)。

最后dp数组的长度就是最长上升子序列的长度。

int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        if(n <= 0)
            return 0;
        vector<int> dp(n+1, 0);
        int len = 0;
        dp[len] = nums[0];
        for(int i = 1; i < n; i ++){
            vector<int>::iterator it = lower_bound(dp.begin(), dp.begin() + len + 1, nums[i]);
            if(it == dp.begin() + len + 1){
                dp[++len] = nums[i];
            }else {
                 *it = nums[i];
            }
        }
        return len + 1;
}

 

 

 

 

 

原文地址:https://www.cnblogs.com/gtarcoder/p/4840829.html