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

本文深入解读动态规划原理,通过最长递增子序列问题,介绍如何利用状态转移和表格存储策略,实现高效求解。作者以[0,1,0,3,2,3]为例,逐步演示动态规划填表过程,并提供清晰的代码实现和解释。

什么是动态规划?

通过组合子问题的解来求解原问题的,动态规划对于每一个子子问题只求解一次,将其解保存在一个表格里面,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作,简单的说,动态规划的特征就是,当前状态的转移依赖于与前一个状态。

详细理解动态规划,点击这里,我觉得这位博主讲的很清楚。

下面将力扣中动态规划题列举出来,做一下分析。

 

 最长递增子序列

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

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

 

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

因为此题符合当前状态的转移依赖于与前一个状态,比如[0,1,3,2,3]中第一个3为例,这个当前3的长度可以在当前1的长度上面加1,而1的长度又依赖于0的长度,而0的长度为1,所以可以回溯求得当前3的长度。可见由小问题可以解决大问题,符合动态规划的特征。

先贴出代码:

public int lengthOfLIS(int[] nums) {
        int len=nums.length;
        int[] dp=new int[len];
        Arrays.fill(dp,1);//把数组中每个元素都是1
        int maxSize=0;
        if (len==1)
            return dp[0];
        for (int i = 1; i <len ; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i]>nums[j]){
                    dp[i]=Math.max(dp[j]+1,dp[i]);
                }
            }
            if (dp[i]>maxSize)
                maxSize=dp[i];
        }
        return maxSize;
    }

其实代码很简单,但是表达起来就比较复杂了,想要大家明白动态规划的流程,我用图来一步一步的描述起来比较麻烦,大家不要吓到了。

下面以[0,1,0,3,2,3]为例,画图来讲解动态规划的过程。

  • 初始化dp数组全为1

  • 从数组第二个数开始遍历,如果第二个数大于第一个数,那么第二个数对应的dp就加1

  • 然后第三个数和前两个数比较,如果每大于一次就加1一次,但是第三个数为0,均不大于前两数,所以dp不变。

然后第四个数和前三个数比较,3>0 ,所以dp[3]=dp[0]+1;

 然后3>1,所以dp[3]=dp[1]+1;

 然后3>0,这个时候要注意了,状态转移方程需要改变了,dp[3]=Math.max(dp[2]+1,dp[3]),因为dp[3]已经有一个更大的值3,到目前为止最长子序列为[0,1,3],如何dp[3]=dp[2]+1的话,序列变为[0,3]并非最长的,而是重新计算的,不是最优解。所以不变。

 然后继续,2和前面的数比较,2>0

2>1

2>0 dp不变

 

然后3和前面的数比较

直到3和2比较时,dp[5]=dp[4]+1;

综上,由此遍历可见,动态规划实际上就是填表的过程。

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值