[笔记]动态规划
最后更新于
最后更新于
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
所以动态规划中每一个状态一定是由上一个状态推导出来的。这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。【动规是由前一个状态推导出来的,而贪心是局部直接选最优的】
所以贪心解决不了动态规划的问题
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化【根据第2步判断】
确定遍历顺序
举例推导dp数组
分析:当前状态由前两个状态推出,可以使用动规
五步走:
确定dp数组(dp table)以及下标的含义:dp[i]表示第i个数字为多少
确定递推公式:dp[i] = dp[i-1] + dp[i-2]
dp数组如何初始化
根据上一步进行初始化
注意题目也给了:dp[0] = 0,dp[1] = 1
确定遍历顺序:dp[i] = dp[i-1] + dp[i-2]可知从前往后遍历
举例推导dp数组
⭐优化:我们只在乎dp[i] 、dp[i-1]、dp[i-2]三个数字,而不需要记录整个序列
分析:主要是由每次可以爬1/2层推出递归公式 + 初始化从1开始
五步走:
确定dp数组(dp table)以及下标的含义:dp[i]表示第i层楼梯有多少种到达方式
确定递推公式:dp[i] = dp[i-1] + dp[i-2]
dp数组如何初始化
根据上一步进行初始化
注意题目也给了:dp[1] = 1,dp[2] = 2
确定遍历顺序:dp[i] = dp[i-1] + dp[i-2]可知从前往后遍历
举例推导dp数组
同样可以优化空间,方法同509
分析:看起来涉及到两个维度:阶梯数&体力,但是这两个维度是联系的(也就是阶梯数对应体力)
五步走:
确定dp数组(dp table)以及下标的含义:dp[i]表示到达第i层花费的最小体力
确定递推公式:dp[i] = min(dp[i-1] + cost[i-1] , dp[i-2] + cost[i-2])
,也计算就是从第i-1层还是第i-2层跳到第i层花费体力小
dp数组如何初始化
你可以选择从下标为 0
或下标为 1
的台阶开始爬楼梯:dp[0] = 0,dp[1] = 0
确定遍历顺序:由递推公式可知从前往后遍历
举例推导dp数组
同样可以优化空间,方法同509
分析:当前点位置路径数 = 左侧位置路径数+上侧位置路径数
五步走:
确定dp数组(dp table)以及下标的含义:dp[i] [j]表示到达(i,j)的路径有多少条
确定递推公式:dp[i] [j] = dp[i] [j-1] +dp[i-1] [j]
dp数组如何初始化
dp[0] [0] = 0
第一行和第一列都是1
确定遍历顺序:由递推公式可知从前往后遍历
举例推导dp数组
优化:状态压缩
分析:总体思路同上,遇到障碍物把当前路径数设为0就好了
五步走:
确定dp数组(dp table)以及下标的含义:dp[i] [j]表示到达(i,j)的路径有多少条
确定递推公式:dp[i] [j] = dp[i] [j-1] +dp[i-1] [j]; if(obstacleGrid[i] [j] == 0) dp[i] [j] = 0
dp数组如何初始化
dp[0] [0] = 0
第一行和第一列都是1
确定遍历顺序:由递推公式可知从前往后遍历
举例推导dp数组
优化:思路同62
顺带嘲笑一下脑子没转过弯来的自己
使用一维不要忘记初始化第一行!!!!!
分析:一开始会一直纠结到底是分成两个数还是几个数,我们统一分成两个数
dp[i]有两种得到方式:
直接拆分:j * (i - j) 【真正意义上的拆分成两个数】
继续拆分:j * dp[i - j] 【这个就相当于把i-j再进行拆分,也就包括了多个数的情况】
五步走:
确定dp数组(dp table)以及下标的含义:dp[i] 表示i可以被拆分的最大乘积
确定递推公式:
dp[i] = max(dp[i] , max((i-j) * j , dp[i-j] * j))
两层max:里面的max是比较直接拆分和继续拆分的大小,外面的是把当前dp[i]和新的计算结果相比
dp数组如何初始化:dp[2] = 1,0和1没有意义
确定遍历顺序:双层遍历
举例推导dp数组
反正最后可以推出dp[i] += dp[j - 1] * dp[i - j],j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量
五步走:
确定dp数组(dp table)以及下标的含义: 1到i为节点组成的二叉搜索树的个数为dp[i]
确定递推公式:
dp[i] += dp[j - 1] * dp[i - j];
j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量
dp数组如何初始化:dp[0] = 1
确定遍历顺序:遍历i里面每一个数作为头结点的状态,用j来遍历。
举例推导dp数组
对于面试,只需要掌握到01背包和完全背包就够了
题目:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
分析:
五步走:
确定dp数组(dp table)以及下标的含义: 即dp[i] [j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
确定递推公式:dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i]);
不放物品i:也就是dp[i - 1] [j],即背包容量为j,里面不放物品i的最大价值,所以背包内的价值依然和前面相同。
放物品i:也就是dp[i - 1] [j - weight[i]] + value[i] 推出
dp[i - 1] [j - weight[i]]
为背包容量为j - weight[i]
的时候不放物品i的最大价值
那么dp[i - 1] [j - weight[i]]
+ value[i]
,就是背包放物品i得到的最大价值
dp数组如何初始化:
首先从dp[i] [j]的定义出发,如果背包容量j为0的话,即dp[i] [0],无论是选取哪些物品,背包价值总和一定为0。
状态转移方程 dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
确定遍历顺序:
先遍历物品还是先遍历背包重量呢?
举例推导dp数组
分析:额有点数学题,难度可以算半个困难了【推导过程见】