动态规划-最长递增子序列

在此之前,我们已经讲解了不少动态规划子数组类型的问题,本篇开始将介绍一些子序列类型问题。

首先,我们需要了解什么是子序列。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序,子序列 包含了 子数组 ,换而言之,对于一个数组,其子数组就是一个子序列,但子序列不都是子数组。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

题目描述

给你一个整数数组 nums ,找到其中最长 严格递增 子序列的长度。
示例:

输入:nums = [10 , 9 , 2 , 5 , 3 , 7 , 91 , 18]

输出:4

解释:最长递增子序列是 [2 , 3 , 7 , 91],因此长度为 4 。

解题思路 

1. 状态定义

首先,我们需要明确状态的定义。在这个问题中,我们定义 dp[i] 为以 nums[i] 结尾的最长递增子序列的长度。这个定义很关键,因为它允许我们仅通过查看数组的前缀(即 nums[0] 到 nums[i-1])来找出以 nums[i] 结尾的最长递增子序列的长度。

2. 初始化

接下来,我们需要初始化 dp 数组。由于任何单独的元素都可以看作是一个长度为 1 的递增子序列,因此我们将 dp 数组的所有元素初始化为 1。这表示每个元素至少可以构成一个长度为 1 的递增子序列。

3. 状态转移

状态转移是动态规划的核心。对于每个位置 i(从 1 到 n-1,其中 n 是数组的长度),我们需要检查它之前的所有位置 j(从 0 到 i-1)。如果 nums[i] > nums[j],那么我们可以尝试将 nums[i] 添加到以 nums[j] 结尾的递增子序列的末尾,从而形成一个更长的递增子序列。为了找到以 nums[i] 结尾的最长递增子序列,我们需要比较所有可能的 dp[j] + 1(其中 j < i 且 nums[i] > nums[j]),并选择其中的最大值来更新 dp[i]

但注意,实际上在遍历过程中,我们并不直接计算这个最大值,而是用一个循环来更新 dp[i]

4. 边界条件

由于我们是从索引 1 开始计算 dp[i] 的(因为 dp[0] 初始化为 1),所以不需要特别处理边界条件。但是,理解这一点很重要:当我们说“以 nums[i] 结尾的最长递增子序列”时,我们实际上是在考虑所有可能的、以 nums[i] 作为最后一个元素的递增子序列。

5. 最终结果

遍历完整个数组后,dp 数组中的最大值就是我们要找的最长递增子序列的长度。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n, 1); // dp[i] 以nums[i]为结尾的最长递增子序列长度 每个元素至少可以构成长度为1的递增子序列 
       
        for (int i = 1; i < n; i++) {

            for (int j = 0; j < i; 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;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值