目录
前言
一、动态规划是什么?
二、解题步骤
三、leetcode动态规划类型
四、leetcode 198题
五、leetcode 53
六、总结
今天正式开始刷leetcode动态规划专项了,我会把动态规划解题的相关注意点也写出来。等动态规划刷的差不多了开启下一系列。
动态规划是将原问题拆成多个子问题然后求解,动态规划保存子问题的解, 避免重复计算。解决动态规划问题的关键是找到状态转移方程,这样我们可以通过计算和储存子问题的解来求解最终问题。
解决该类问题要清楚:
1、递推公式推导,动态规划解题的关键也是难点。回到动态规划的定义上:动态规划是将原问题拆成多个子问题然后求解,动态规划保存子问题的解, 避免重复计算。递推公式就是由子问题推到而来,在解题的时候分析出如何由子问题推导出父问题基本可以解决递推公式的问题。
2、dp数组的定义以及下标的含义,例如dp[i][j],这个二维数组的值是什么意思,i,j又代表什么。
3、dp数组如何初始化,初始化也需要注意,对于一维dp数组一般是初始化前几个位置;对于二维数组一般是初始化前几行或者前几列。
4、遍历顺序,比较考究。比如背包问题先遍历背包还是先遍历物品,有时候两种遍历顺序都行,有时候只有指定顺序才能成功。
4.1排列和组合的遍历顺序是不相同的。
4.1.1 排列:背包在外 物品在内。(322)
4.1.2 组合:物品在外,背包在内。(518)
5.如果按照上述步骤解题出现错误可以打印dp数组,检查是否有和自己预期的一样,如果不一样检查1-4步哪里出错了,如果预期一样那么要考虑是不是递推公式有问题。
leetcode中关于动态规划的提醒大概有以下几类:
1、斐波那契数列或爬楼梯问题,该问题可以看我的上一篇博客。
2、打家劫舍问题
3、子序列问题
4、背包问题
5、股票问题
198. 打家劫舍
题目描述:你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
①递推公式推导:我们从数组的左边开始遍历,假设当前小偷来到了i号房间,小偷可以选择偷或者不偷该房间:
1、如果选择偷该i号房间,那么偷完i号房的钱 money[i] = money[i] + money[i-2],因为不能偷相邻房间。
2、如果不偷i号房间,那么money[i] = money[i-1] 。
要尽量多的钱显然要取两个1、2选择中最大的那个。
由此可以得到递推公式:
dp[i] = (dp[i-1] > (nums[i] + dp[i-2]))?dp[i-1]:(nums[i] + dp[i-2]);
②初始化:显然当小偷在第0个房间时偷的钱为数组第一个元素的值。第二个及以后的房间按照①中的步骤进行选择。
源码如下:
int rob(int* nums, int numsSize){
int dp[100] = {0};
dp[0] = nums[0];
int i = 0;
for(i = 1; i < numsSize; ++i)
{
if(1 == i)
{
dp[i] = nums[0] > nums[1]?nums[0]:nums[1];
continue;
}
dp[i] = (dp[i-1] > (nums[i] + dp[i-2]))?dp[i-1]:(nums[i] + dp[i-2]);
}
return dp[numsSize - 1];
}
最大子数组和
给你一个整数数组
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6。
解题思路: 这题思路和上题类似。
①递推公式: 假设要求以第i个元素(假设值为a[i])为结尾的最大连续子数组的和sum[i],则该位置的和与前一个元素i-1位置的最大连续子数组的和sum[i-1]有关,如果想要第i个位置与前一位置构成新的最大连续子序列需要sum[i-1] + a[i] >= sum[i-1],此时sum[i] = sum[i-1] + a[i];否则sum[i] = a[i]。
②初始化:显然第0个位置的最大连续子序列和为a[0]。
源码如下:
int maxSubArray(int* nums, int numsSize){
int p[100000] = {0};
p[0] = nums[0];
int max = nums[0];
int i = 0, j = 0;
for(i = 1; i < numsSize; ++i)
{
if(p[i-1] + nums[i] <= nums[i] )
{
p[i] = nums[i];
}
else
{
p[i] = nums[i] + p[i-1];
}
}
for(i = 0; i < numsSize; ++i)
{
if(max < p[i])
{
max = p[i];
}
}
return max;
}
上面两个题目还属于简单点的动态规划题目,代码写的还有点粗糙,有可以优化的空间。我们可以对动态规划进行空间压缩,使得原来的 O(n) 空间复杂度优化为 O(1) 复杂度。另外解题思路确实是难以想到,牢牢把握动态规划的思想:拆成多个子问题然后求解;有利于递推公式的推导。需要多做练习才能更加熟练。
我的leetcode解题仓库:leetcode: 记录leetcode刷题思路和源码 (gitee.com) 按照题目类别分类并记录思路。