最长上升子序列问题(动态规划)

1、什么是最长上升子序列?

最长上升子序列(Longest Increasing Subsequence),简称LIS,是指在一个序列中找到一个严格递增的子序列,其长度最大。例如,给定序列[10,9,2,5,3,7,101,18],则其最长上升子序列为[2,3,7,101],长度为4。

2、如何去写其状态转移方程?

LIS问题是一个经典的动态规划问题,其状态转移方程如下:

dp[i] = max(dp[i],dp[j] + 1),其中j是0到i-1之间的任意一个数,且nums[j] < nums[i]

意思是dp[i]表示以第i个数字结尾的最长上升子序列的长度

2.1 初始值

初始时,dp数组中的每个元素都被设为1,因为单独一个数字也是一个长度为1的上升子序列。

2.2 状态转移方程

在计算dp[i]时,需要枚举i前面的所有数字j,如果nums[j] < nums[i],那么dp[i] = max(dp[i], dp[j] + 1),也就是选择第j个数字作为i的前一个数字时,以j结尾的最长上升子序列的长度+1就是以i结尾的最长上升子序列的长度。

2.3 最终结果

最终,dp数组中的最大值即为整个序列中的最长上升子序列长度。

3、可以手算一下:

序列:10, 9, 2, 5, 3, 7, 101, 18

初始化:dp[0]=dp[1]=dp[2]=dp[3]=dp[4]=dp[5]=dp[6]=dp[7]=1

计算dp[1]:枚举j=0,不满足nums[j] < nums[i](因为10>9),dp[1] =1

计算dp[2]:枚举j=0,不满足nums[j] < nums[i](因为10>2),dp[2] =1;枚举j=1,不满足nums[j] < nums[i](因为9>2),dp[2] =1

计算dp[3]:枚举j=0,不满足nums[j] < nums[3](因为10>5);枚举j=1,不满足nums[j] < nums[3](因为9>5);枚举j=2,满足nums[j] < nums[3](2<5),dp[3]=max(dp[3],dp[2]+1)=max(1,2)=2

计算dp[4]:枚举j=0,不满足nums[j] < nums[4](因为10>3);枚举j=1,不满足nums[j] < nums[4](因为9>3);枚举j=2,满足nums[j] < nums[4](因为2<3),dp[4] = max(dp[4], dp[2]+1) = 2;枚举j=3,不满足nums[j] < nums[4]

计算dp[5]:枚举j=0,不满足nums[j] < nums[5];枚举j=1,不满足nums[j] < nums[5];枚举j=2,满足nums[j] < nums[5](2<7),dp[5] = max(dp[5], dp[2]+1) = 2;枚举j=3,满足nums[j] < nums[5],dp[5] = max(dp[5], dp[3]+1) = 3;枚举j=4,满足nums[j] < nums[5](3<7),dp[5] = max(dp[5], dp[4]+1) = 3

计算dp[6]:枚举j=0,满足nums[j] < nums[6](10<101),dp[6] = max(dp[6], dp[0]+1) = 2;枚举j=1,满足nums[j] < nums[6](9<101),dp[6] = max(dp[6], dp[1]+1) = 2;枚举j=2,满足nums[j] < nums[6],dp[6] = max(dp[6], dp[2]+1) = 2;枚举j=3,5<101,dp[6] = max(dp[6], dp[3]+1) = 3;枚举j=4,3<101,dp[6] = max(dp[6], dp[4]+1) = 3;枚举j=5,7<101,dp[6] = max(dp[6], dp[5]+1) = 4

计算dp[7]:枚举j=0,10 <18,dp[7] = max(dp[7], dp[0]+1) = 2;枚举j=1,9 < 101,dp[7] = max(dp[7], dp[1]+1) = 2;枚举j=2,满足2 < 10,dp[7] = max(dp[7], dp[2]+1) = 2;枚举j=3,满足nums[j] < nums[7],5<18,dp[7] = max(dp[7], dp[3]+1) = 3;枚举j=4,3<18,dp[7] = max(dp[7], dp[4]+1) = 3;枚举j=5,7<18,dp[7] = max(dp[7], dp[5]+1) = 4;枚举j=6,不满足nums[j] < nums[7]

所以最后的结果是dp数组中的最大值,即4

4、给出C++代码实现:

int lengthOfLIS(vector<int>& nums) {
    int n = nums.size();
    vector<int> dp(n, 1); //初始化dp数组
    int res = 1;
    for(int i=1;i<n;i++) {
        for(int j=0;j<i;j++) {
            if(nums[j] < nums[i]) {
                dp[i] = max(dp[i], dp[j]+1); //状态转移方程
            }
        }
        res = max(res, dp[i]);
    }
    return res;
}

该算法的时间复杂度为O(n^2),空间复杂度为O(n)。

除了动态规划思路外,还有一种基于二分查找的算法,可以将时间复杂度缩减至O(nlogn),有兴趣的读者可以自行探索。

猜你喜欢

转载自blog.csdn.net/m0_52124992/article/details/129859634