01背包问题 是动态规划中最经典的入门问题。它的核心是:每种物品只有一件,要么选(1),要么不选(0),在背包容量有限的情况下,让总价值最大。
1. 问题描述
-
有
N件物品,背包容量为V -
第
i件物品:重量w[i],价值v[i] -
每件物品只能选一次
-
求不超过背包容量的最大价值
2. 核心思路(动态规划)
定义:dp[j] = 容量为 j 的背包能装的最大价值
状态转移方程(一维优化版):
text
dp[j] = max(dp[j], dp[j - w[i]] + v[i])
-
不选第
i件:dp[j]保持不变 -
选第
i件:容量减少w[i],价值增加v[i] -
取两者最大值
3. 关键注意事项
遍历顺序必须从大到小(逆序)
原因:一维数组会覆盖历史数据,逆序保证 dp[j - w[i]] 用的是**上一轮(未选当前物品)**的状态,避免同一件物品被重复选取(完全背包才正序)。
4. C++ 完整代码
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N, V;
cin >> N >> V; // 物品数量,背包容量
vector<int> w(N + 1), v(N + 1);
for (int i = 1; i <= N; i++) {
cin >> w[i] >> v[i];
}
// dp[j] 表示容量为 j 的背包能装的最大价值
vector<int> dp(V + 1, 0);
// 遍历每件物品
for (int i = 1; i <= N; i++) {
// 逆序遍历容量,从 V 到 w[i]
for (int j = V; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
cout << dp[V] << endl;
return 0;
}
5. 输入输出示例
text
输入:
4 5
1 2
2 4
3 4
4 5
输出:
8
(选择物品1和3,重量1+3=4,价值2+4=6;或物品2和3,重量2+3=5,价值4+4=8)
6. 两种写法对比
| 写法 | 数组维度 | 遍历顺序 | 空间复杂度 |
|---|---|---|---|
| 二维dp | dp[N+1][V+1] |
正序 | O(NV) |
| 一维dp(滚动数组) | dp[V+1] |
容量逆序 | O(V) |
一维写法是竞赛中最常用的,必须掌握。
7. 常见易错点
-
❌ 容量循环写成正序 → 变成完全背包(物品可重复选)
-
❌ 内层循环从
0到V→ 同样错误 -
❌ 忘记初始化
dp为 0 → 默认值要保证正确 -
❌ 边界条件:
j >= w[i]才更新,否则越界
8. 扩展:如果需要恰好装满
初始化时:
cpp
dp[0] = 0;
for (int j = 1; j <= V; j++) dp[j] = -INF;
最后判断 dp[V] < 0 则无法恰好装满。