背包DP简介
背包DP是一类经典的动态规划问题,核心思想是通过状态转移方程求解最优解。典型问题包括01背包、完全背包、多重背包等,用于解决有限容量下的物品选择问题。
01背包问题
问题描述:给定容量为W的背包和N个物品,每个物品有重量w[i]和价值v[i]。每个物品只能选或不选,求最大价值。
状态定义
dp[i][j]表示前i个物品在容量j下的最大价值。
状态转移方程
cpp
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
空间优化
使用一维数组逆序更新:
cpp
for (int i = 1; i <= N; ++i) {
for (int j = W; j >= w[i]; --j) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
完全背包问题
问题描述:与01背包类似,但每个物品可以选无限次。
状态转移方程
cpp
dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i]);
空间优化
正序更新一维数组:
cpp
for (int i = 1; i <= N; ++i) {
for (int j = w[i]; j <= W; ++j) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
多重背包问题
问题描述:每个物品有数量限制s[i],可转化为01背包或使用二进制优化。
二进制优化
将s[i]拆分为1, 2, 4, ..., 2^k的组合:
cpp
for (int i = 1; i <= N; ++i) {
int cnt = s[i];
for (int k = 1; k <= cnt; k *= 2) {
cnt -= k;
for (int j = W; j >= k * w[i]; --j) {
dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i]);
}
}
if (cnt > 0) {
for (int j = W; j >= cnt * w[i]; --j) {
dp[j] = max(dp[j], dp[j - cnt * w[i]] + cnt * v[i]);
}
}
}
代码示例(01背包完整实现)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N = 5, W = 10;
vector<int> w = {0, 2, 3, 4, 5, 6}; // 重量(0占位)
vector<int> v = {0, 3, 4, 5, 6, 7}; // 价值(0占位)
vector<int> dp(W + 1, 0);
for (int i = 1; i <= N; ++i) {
for (int j = W; j >= w[i]; --j) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
cout << "Max value: " << dp[W] << endl;
return 0;
}
关键点总结
- 01背包:逆序更新,避免重复选择。
- 完全背包:正序更新,允许重复选择。
- 多重背包:二进制优化降低时间复杂度。
- 初始化 :根据问题需求设置
dp[0] = 0或其他初始值。