什么是动态规划?
通过组合子问题的解来求解原问题的,动态规划对于每一个子子问题只求解一次,将其解保存在一个表格里面,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作,简单的说,动态规划的特征就是,当前状态的转移依赖于与前一个状态。
详细理解动态规划,点击这里,我觉得这位博主讲的很清楚。
下面将力扣中动态规划题列举出来,做一下分析。
给你一个整数数组
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;

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

被折叠的 条评论
为什么被折叠?



