【喝水不忘挖井人】
代码随想录
动态规划五部曲:
1.明确dp[i][j]或dp[j]代表的具体意义以及下标的含义
2.写出递推公式
3.dp[]的初始化
4.确定遍历顺序
5.举例推导数组
背包
0-1背包问题
- 二维数组, 内外层都是正序遍历 物品和容量内外层无所谓
- 一维数组, 物品正序, 容量倒序
物品在外层,容量在内层
for (int i = 0; i < nums.length; i++) { for (int j = bound; j > nums[i]; j--) { .... } }
题型:
-
把数组分成两部分:根据题意找到dp的大小 --> dp[j] = Math.max(dp[j], dp[j-nums[i]]+nums[i]);
-
求组合数 --> dp[j] += dp[j-nums[i]];
【注】int[][] dp = new int[m+1][n+1]; for (String s : strs) { int zeroNum = 0; int oneNum = 0; for (char c : s.toCharArray()) { if (c == '0') { zeroNum++; }else { oneNum++; } } for (int i = m; i >= zeroNum; i--) { for (int j = n; j >= oneNum; j--) { dp[i][j] = Math.max(dp[i][j], dp[i-zeroNum][j-oneNum]+1); } } }
虽然dp是二维数组,但是i和j是两个不同的维度,所以单独每一都相当于是一维数据,外层是物品,这两个维度的遍历顺序必须是倒序,否则会重复选择
完全背包
一维数组:物品和容量内外层无所谓, 都是正序
求最小值时dp通常初始化为Integer.MAX_VALUE
题型:
- 组合数 外层物品,内层容量
- 排列数 外层容量,内层物品 【如果要列出排列只能用dfs】
多重背包
有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci ,价值是Wi 。
例如:
背包最大重量为10。
物品为:
重量 | 价值 | 数量 | |
---|---|---|---|
物品0 | 1 | 15 | 2 |
物品1 | 3 | 20 | 3 |
物品2 | 4 | 30 | 2 |
问背包能背的物品最大价值是多少?
和如下情况有区别么?
重量 | 价值 | 数量 | |
---|---|---|---|
物品0 | 1 | 15 | 1 |
物品0 | 1 | 15 | 1 |
物品1 | 3 | 20 | 1 |
物品1 | 3 | 20 | 1 |
物品1 | 3 | 20 | 1 |
物品2 | 4 | 30 | 1 |
物品2 | 4 | 30 | 1 |
毫无区别,这就转成了一个01背包问题了,且每个物品只用一次。
方法:
-
把每种商品遍历的个数放在01背包里面在遍历一遍
-
在循环内部遍历个数
for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量 // 以上为0-1背包,然后加一个遍历个数 for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数 dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]); } } }
股票
>dp[i][0]:买入状态
>dp[i][1]:卖出状态
-
只能交易一次:
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+prices[i]); dp[i][1] = Math.max(dp[i-1][1], -prices[i]);
-
不限次数
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+prices[i]); dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-prices[i]);
-
交易两次
dp[i][0] = Math.max(dp[i-1][0], -prices[i]); dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]+prices[i]); dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]-prices[i]); dp[i][3] = Math.max(dp[i-1][3], dp[i-1][2]+prices[i]);
-
冷冻期
// 0-买入 1-两天前卖出 2-一天前卖出 3-冷冻期 int[][] dp = new int[prices.length][4];
数组序列
数组序列
-
两个数组/序列匹配相等的情况, 通常设dp
int[][] dp = new int[len1 + 1][len2 + 1]; boolean[][] dp = new boolean[len][len]; // 回文 时是boolean类型
-
dp定义:有时可以考虑dp的索引是数组或序列中元素的位置,而不是索引
-
初始化
:根据题目考虑每个元素的初始值 Arrays.fill(dp, 1); 不是初始化一个dp[0]就结束了!! -
递推公式:
- 连续的范围:
只有满足条件
时才更新dp[i] —> 包含该位置的元素dp的取值 - 不要求连续:
满足/不满足
都要更新dp[i] —> 到该元素位置的子集能取到的dp的值(包含/不包含)
- 连续的范围:
公共子序列模板
for (int i = 1; i <= sLen; i++) {
for (int j = 1; j <= tLen; j++) {
if (sc[i-1] == tc[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
}else {
// 根据是否要求连续判断是否有这个语句
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}