解决问题
- 动态规划基础问题
- 背包问题
- 打家劫舍问题
- 股票问题
- 子序列问题
贪心算法与动态规划算法
- 贪心:局部最优推导全剧最优
- 动态规划:由前(后)几个状态依次推导出其余状态
动态规划五部曲
- 明确下标含义及 dp 数组含义
- 递推公式
- dp数组初始化
- 遍历顺序
- 打印 dp 数组
01背包问题:务必手动模拟
描述 | 重量 | 价值 |
---|---|---|
物品0 | 1 | 15 |
物品1 | 3 | 20 |
物品2 | 4 | 30 |
背包最大重量为4.
二维数组实现01背包
- dp[ i ][ j ] 含义:[ 0, i ] 物品任取,存放容量为 j 的背包的最大价值;
- 递推公式:
dp[ i ][ j ] = max( dp[ i - 1 ][ j ] , dp[ i - 1 ][ j - weight[ i ] ] + value [ i ] )
- 不放物品 i :背包容量为 j , 所能装最大价值为 dp[ i - 1 ][ j ];
- 放物品 i :背包容量为 j , 所能装最大价值为dp[ i - 1 ][ j - weight[ i ] ] + value [ i ];(背包容量倒退,直到可以留出空间给物品i ) - 初始化:由递推公式可知,需要初始化 dp[ 0 ][ j ], dp[ i ][ 0 ];
dp[ 0 ][ j ] = value[ 0 ] ;
dp[ i ][ 0 ] = 0 ;
- 遍历顺序:对于二维数组实现的01背包,两层 for 循环可以颠倒
- 遍历物品(更好)
- 遍历背包
一维(滚动)数组实现01背包:滚动数组由二维数组压缩而来
- dp[ j ] 含义:容量为 j 的背包的最大价值;
- 递推公式:
dp[ j ] = max( dp[ j ] , dp[ j - weight[ i ] ] + value [ i ] )
- 不放物品 i :背包容量为 j , 所能装最大价值为 dp[ j ];
- 放物品 i :背包容量为 j , 所能装最大价值为dp[ j - weight[ i ] ] + value [ i ]; - 初始化:dp 数组全部为 0
- 遍历顺序:
- 倒序:保证每个物品只添加一次。本质上还是对二维数组的遍历,并且右下角的值依赖于上一层左上角的值,因此需要保证左边的值仍然是上一层的,从右向左覆盖。
- 先物品再背包
装满背包有多少种方法?
递归公式:dp[ j ] += nums[ j - nums[ i ] ];
完全背包理论基础
完全背包与01背包区别:
- 01背包:每件物品使用1次:倒序
- 完全背包:每件物品使用无数次:正序(纯完全背包问题下:物品与背包遍历顺序可以颠倒)
关键代码
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
组合与排列问题
- 求组合:
- 外层for:物品
- 内层for:背包
- 求排列:
- 外层for:背包
- 内层for:物品
动态规划基础问题
509. 斐波那契数
70. 爬楼梯
746. 使用最小花费爬楼梯
62. 不同路径
63. 不同路径 II
343. 整数拆分
- dp数组极其下标含义:数 i 拆分后最大值为 dp[ i ]
- 递归公式:
dp[ i ] = max(dp [ i ], j * ( i - j ), j * dp[ i - j ] );
- j * ( i - j )为拆为两数情况
- j * dp[ i - j ]为拆为三数及以上情况
- dp[i]为每轮保存最大值;
96. 不同的二叉搜索树
- dp数组极其下标含义:由i个节点组成的不同二叉搜索树有dp[i]种
- 递归公式:
dp[ i ] += dp[ j - 1 ] * dp[ i - j ];
- dp[j - 1]为左子树种类;
- p[i - j]为右子树种类。
背包问题
01背包问题
416. 分割等和子集
- 难点:将问题抽象成01背包:元素不能重复使用——01背包
- 物品:数组元素,物品重量 = 价值 = nums[i];
- 背包容量:sum / 2;
1049. 最后一块石头的重量 II
- 难点:将问题抽象成01背包:将数组分成两边,两边和越接近,越满足最后结果。同上题一样。
494. 目标和:dp[j] 代表装满背包有几种方法,区别于普通01背包求最大价值。
- 难点:问题转化。将数组分成两部分,一部分为正数,一部分为负数。转化为背包问题。
- dp[j]含义:装满容量为 j 的背包,有dp[j]种方法
- 递推公式:
dp[j] += dp[j - nums[i]];
- 初始化: dp[0] = 1, 其余为 0.
474. 一和零
- 难点:物品重量:两个维度0和1
- dp[i][j]含义:i个0,j个1时,最大子集长度为 dp[i][j]
- 递推公式:
dp[i][j] = max(dp[i][j], dp[i - x][j - y] + 1);