背包模型的 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 被选

posted @ 2025-06-17 22:07  kkman2000  阅读(73)  评论(0)    收藏  举报