Skip to content

最长递增子序列

动态规划的难点本来就在于寻找正确的状态转移方程,借助经典的「最长递增子序列问题」来讲一讲设计动态规划的通用技巧:数学归纳思想

最长递增子序列这个问题,首先要定义清楚 dp 数组的含义,即 dp[i] 的值到底代表着什么?

我们的定义是这样的:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度

根据这个定义,我们就可以推出 base case:dp[i] 初始值为 1,因为以 nums[i] 结尾的最长递增子序列起码要包含它自己。

根据刚才我们对 dp 数组的定义,现在想求 dp[5] 的值,也就是想求以 nums[5] 为结尾的最长递增子序列。

nums[5] = 3,既然是递增子序列,我们只要找到前面那些结尾比 3 小的子序列,然后把 3 接到最后,就可以形成一个新的递增子序列,而且这个新的子序列长度加一

class Solution {
    public int lengthOfLIS(int[] nums) {
        // 建立dp数组,表示以nums[i]结尾的最长递增子序列的长度
        int[] dp = new int[nums.length];
        // base case,以nums[i]结尾的最长递增子序列起码包括它本身
        Arrays.fill(dp,1);
        // 遍历nums数组,计算每个dp的值
        for(int i = 0;i<nums.length;i++){
            // 遍历走过数的所有数
            for(int j = 0; j < i; j++){
                // 如果之前的某个数小于当前这个数,则在其dp[j]的基础上加1,与当前dp[i]进行比较,取大数
                if(nums[i]>nums[j]){
                    dp[i] = Math.max(dp[i],dp[j]+1);
                }
            }
        }
        // 取出dp数组中的最大数
        int res = 0;
        for(int i = 0;i<dp.length;i++){
            res = Math.max(res,dp[i]);
        }
        return res;
    }
}