代码随想录 | 刷题-动态规划4
01背包问题中一维和二维 dp 的遍历顺序
一维 dp:倒序遍历容量
- 原因:倒序遍历可以保证每个物品只被放入一次。
- 原理:每次更新
dp[j]
时,使用的是上一轮(未加入当前物品)的状态,避免重复加入同一个物品。 - 举例:
- 物品0,重量1,价值15
- 正序遍历:
dp[2] = dp[1] + 15 = 30
(物品0被加了两次,错误!) - 倒序遍历:
dp[2] = dp[1] + 15 = 15
,dp[1] = dp[0] + 15 = 15
(每个容量只加一次物品0)
- 物品0,重量1,价值15
二维 dp:正序遍历容量
- 原因:二维 dp 的
dp[i][j]
都是通过上一层dp[i-1][j]
计算的,本层不会覆盖上一层,所以不会重复加入物品。 - 原理:每一层代表一个物品,状态转移只依赖于上一层,天然保证每个物品只选一次。
1049. 最后一块石头的重量 II
- 目标:将所有石头分成两组,使两组总重量尽量接近,最后剩下的石头重量就是两组重量差的绝对值。
- 转化为01背包问题:每个石头只能选一次,背包容量为所有石头重量和的一半,尽量让一组的重量最大且不超过容量。
1 | class Solution { |
494. 目标和
该问题可以转化为一个子集和问题:在数组中找到一些数字,使它们的和等于
(target + sum(nums)) / 2
(记为
bagSize
),每个数字只能使用一次。以下是详细步骤:
- 问题转化:
- 设加法总和为
x
,则减法总和为sum - x
。 - 根据条件
x - (sum - x) = target
,解得x = (target + sum) / 2
。 - 问题转化为:在
nums
中选取若干数字,使它们的和等于bagSize
,求选取方案数。
- 设加法总和为
- 边界条件:
- 若
abs(target) > sum
,无解。 - 若
(target + sum)
为奇数,无解(bagSize
必须是整数)。
- 若
- 动态规划:
- 使用一维数组
dp[j]
表示装满容量j
的背包的方案数。 - 初始化
dp[0] = 1
(空集方案)。 - 遍历每个数字,逆序更新
dp
数组(避免重复使用数字)。
- 使用一维数组
1 | class Solution { |
代码解释
- 初始化:
dp[0] = 1
:表示容量为0的背包有1种方案(不选任何数字)。- 其他位置初始化为0。
- 遍历更新:
- 对每个数字
num
,从bagSize
到num
逆序遍历。 - 更新公式:
dp[j] += dp[j - num]
,表示:- 不选
num
达成j:方案数保持dp[j]
。 - 选
num
达成j:方案数加上dp[j - num]
(剩余容量j - num
的方案数)。
- 不选
- 对每个数字
- 示例:
nums = [1, 1, 1, 1, 1]
,target = 3
:total = 5
,bagSize = (3 + 5) / 2 = 4
。dp
数组初始为[1, 0, 0, 0, 0]
。- 遍历每个
1
后,dp[4] = 5
(5种组合方式)。
复杂度
- 时间复杂度:O(n × bagSize),其中
n
为数组长度。 - 空间复杂度:O(bagSize)。
474.一和零
本题容易被看成多重背包问题,识别清楚问题本质就容易做了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class Solution {
public:
vector<int> count(string s){
vector<int> cnt;
int zeroes = 0, ones = 0;
for(char c:s){
if(c == '0')zeroes++;
if(c == '1')ones++;
}
cnt.push_back(zeroes);
cnt.push_back(ones);
return cnt;
}
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for(int i = 0; i<strs.size();i++){
vector<int> cnt = count(strs[i]);
if(cnt[0] > m || cnt[1] > n){
continue;
}
for(int a = m; a >= cnt[0]; a--){
for(int b = n; b >= cnt[1]; b--){
dp[a][b] = max(dp[a][b], dp[a-cnt[0]][b-cnt[1]] + 1);
}
}
}
return dp[m][n];
}
};