目录
🌟 背包问题核心思想
状态定义:dp[i][j]
表示前i个物品,在容量j的限制下获得的最大价值
核心方程:dp[i][j] = max(选当前物品策略,不选策略)
空间优化:绝大多数情况可用一维数组倒序遍历实现
📌 0-1背包模板
特点:每个物品只能选0/1次
倒序遍历容量是关键!
vector<int> dp(bagSize + 1);
for(int i = 0; i < n; ++i){
for(int j = bagSize; j >= weight[i]; --j){
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
🔄 完全背包模板
特点:物品无限次选取
正序遍历实现重复选择
vector<int> dp(bagSize + 1);
for(int i = 0; i < n; ++i){
for(int j = weight[i]; j <= bagSize; ++j){
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
🎲 多重背包模板
特点:物品最多选k次 ▶️ 二进制优化版本
// 二进制拆分预处理
vector<int> new_weights, new_values;
for(auto &item : items){
int cnt = item.max_count;
for(int k=1; k<=cnt; k*=2){
new_weights.push_back(item.weight * k);
new_values.push_back(item.value * k);
cnt -= k;
}
if(cnt > 0){
new_weights.push_back(item.weight * cnt);
new_values.push_back(item.value * cnt);
}
}
// 0-1背包过程(略)
🧩 混合背包模板
特点:0-1/完全/多重混合 ▶️ 类型判断+分支处理
for(每个物品){
if(0-1背包类型)
倒序遍历容量
else if(完全背包)
正序遍历容量
else if(多重背包)
二进制拆分后按0-1处理
}
🌐 二维费用背包模板
特点:同时限制重量/体积两个维度
vector<vector<int>> dp(weightSize+1, vector<int>(volumeSize+1));
for(int i=0; i<n; ++i){
for(int j=weightSize; j>=weight[i]; --j){
for(int k=volumeSize; k>=volume[i]; --k){
dp[j][k] = max(dp[j][k],
dp[j-weight[i]][k-volume[i]] + value[i]);
}
}
}
🎯 恰好装满问题
初始化技巧:
dp[0] = 0; // 初始状态合法
for(int i=1; i<=bagSize; ++i)
dp[i] = -INF; // 非法状态
📊 方案数统计模板
递推式:dp[j] += dp[j - weight[i]]
vector<int> dp(bagSize+1, 0);
dp[0] = 1; // 初始方案数
for(int i=0; i<n; ++i){
for(int j=bagSize; j>=weight[i]; --j){
dp[j] += dp[j - weight[i]];
}
}
💡 关键总结
-
遍历顺序定乾坤:0-1背包倒序,完全背包正序
-
状态初始化:普通问题初始化0,恰好装满需特殊处理
-
复杂度控制:多重背包必须二进制优化
-
维度扩展:二维费用只需增加状态维度
🚀 掌握这些模板,LeetCode背包问题直接默写!