背包模型的 C++ 模板代码模版与思路参考( from 黄老师)
背包模型的 C++ 模板代码模版与思路参考( from 黄老师)
下面是各类常见 背包模型的 C++ 模板代码,包含了:
- 求最大价值(标准)
- 求方案数(计数型 DP)
✅ 1. 0-1 背包(01 Knapsack)
功能:
- 每个物品只能选一次;
- 求最大价值 & 方案数(最多多少种方法装满)。
// 最大价值 + 方案数
const int MOD = 1e9 + 7;
int n, W; // 物品数量,总容量
int w[N], v[N]; // 重量、价值
int dp[N]; // 最大价值
int cnt[N]; // 方案数
void zeroOneKnapsack() {
memset(dp, 0, sizeof dp);
memset(cnt, 0, sizeof cnt);
cnt[0] = 1; // 容量为0的初始方案数
for (int i = 1; i <= n; ++i) {
for (int j = W; j >= w[i]; --j) {
if (dp[j] < dp[j - w[i]] + v[i]) {
dp[j] = dp[j - w[i]] + v[i];
cnt[j] = cnt[j - w[i]]; // 新最大值来源于此
} else if (dp[j] == dp[j - w[i]] + v[i]) {
cnt[j] = (cnt[j] + cnt[j - w[i]]) % MOD;
}
}
}
// 最优值是 dp[W],方案数是 cnt[W](或遍历所有容量,找最大值方案)
}
✅ 2. 完全背包(Complete Knapsack)
功能:
- 每个物品可选无限次;
- 求最大价值 & 方案数。
void completeKnapsack() {
memset(dp, 0, sizeof dp);
memset(cnt, 0, sizeof cnt);
cnt[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = w[i]; j <= W; ++j) {
if (dp[j] < dp[j - w[i]] + v[i]) {
dp[j] = dp[j - w[i]] + v[i];
cnt[j] = cnt[j - w[i]];
} else if (dp[j] == dp[j - w[i]] + v[i]) {
cnt[j] = (cnt[j] + cnt[j - w[i]]) % MOD;
}
}
}
}
✅ 3. 多重背包(Bounded Knapsack)
功能:
- 每个物品有个数限制;
- 朴素做法是套循环;
- 优化版用 二进制优化。
// 二进制优化:将每个物品拆成 log(k) 个 0-1 背包
void multipleKnapsackBinary() {
vector<int> items_w, items_v;
for (int i = 1; i <= n; ++i) {
int k = s[i]; // 件数
for (int b = 1; k > 0; b <<= 1) {
int num = min(b, k);
items_w.push_back(w[i] * num);
items_v.push_back(v[i] * num);
k -= num;
}
}
memset(dp, 0, sizeof dp);
memset(cnt, 0, sizeof cnt);
cnt[0] = 1;
for (int i = 0; i < items_w.size(); ++i) {
for (int j = W; j >= items_w[i]; --j) {
if (dp[j] < dp[j - items_w[i]] + items_v[i]) {
dp[j] = dp[j - items_w[i]] + items_v[i];
cnt[j] = cnt[j - items_w[i]];
} else if (dp[j] == dp[j - items_w[i]] + items_v[i]) {
cnt[j] = (cnt[j] + cnt[j - items_w[i]]) % MOD;
}
}
}
}
✅ 4. 分组背包(Grouped Knapsack)
功能:
- 每组中最多选一个物品;
- 每组多个物品,按组来选择。
struct Item {
int w, v;
};
vector<Item> group[N]; // 第 i 组所有物品
void groupKnapsack() {
memset(dp, 0, sizeof dp);
memset(cnt, 0, sizeof cnt);
cnt[0] = 1;
for (int g = 1; g <= G; ++g) { // G 是组数
for (int j = W; j >= 0; --j) {
int best = dp[j];
int ways = cnt[j];
for (auto &item : group[g]) {
if (j >= item.w) {
int val = dp[j - item.w] + item.v;
if (val > best) {
best = val;
ways = cnt[j - item.w];
} else if (val == best) {
ways = (ways + cnt[j - item.w]) % MOD;
}
}
}
dp[j] = best;
cnt[j] = ways;
}
}
}
✅ 5. 背包方案数(完全/01)
如果只统计装满容量为 W 的总方案数:
🧮 01背包计数(只求方案数):
int dp[N] = {1}; // dp[0]=1 表示总容量为0的方案只有1种
for (int i = 1; i <= n; ++i)
for (int j = W; j >= w[i]; --j)
dp[j] = (dp[j] + dp[j - w[i]]) % MOD;
🧮 完全背包计数:
int dp[N] = {1}; // 同上
for (int i = 1; i <= n; ++i)
for (int j = w[i]; j <= W; ++j)
dp[j] = (dp[j] + dp[j - w[i]]) % MOD;
✅ 6. 背包路径还原(选了哪些物品)
若要记录选了哪些物品,需记录决策路径,可在模板中加:
int path[N]; // path[j] 表示容量 j 来自哪个物品(或哪个状态)
或者使用二维数组:
bool choice[N][N]; // choice[i][j]: 第 i 件物品是否在容量 j 被选

浙公网安备 33010602011771号