背包问题

背包问题

dp[i][j] 表示使用前i个物品,当前体积为j时 能取得的最大价值

for (枚举单个/单组物品) {
    for (枚举体积) {
        for (枚举选择) { // 选/不选 单个/单组物品
            if (满足条件) {
                记录结果
            }
        }
    }
}

恰好装满

初始化 含义
dp[0][0] = 0 使用前0个物品,恰好装满体积j=0,能得到最大价值0
dp[0][j > 0] = -inf 使用前0个物品,永远无法装满体积j > 0
dp[i > 0][0] = -inf 使用前i > 0个物品,永远无法恰好装满j=0,体积一定会超过0

*为防止溢出,-inf取-9999999即可

01背包的一维表示

2D状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i])

画图:dp[i][j]只依赖于上一行(i-1)的当前列j,和上一行(i-1)的j左侧某列j-w[i]

则压缩成1D时,需要从右向左逆序遍历列j,以确保左侧的j-w[i]保留为上一行的结果,而不是被本行i的结果覆盖:

dp[j] = max(dp[j], dp[j - w[i]] + v[i])

完全背包

由于每件物品i可以被选择多次,因此某个dp[i][j]的值应该为选i不超过容量限制j的最大值:

选择 0 件物品 i 的最大价值 = dp[i-1][j - 0 * w[i]] + 0 * v[i]
选择 1 件物品 i 的最大价值 = dp[i-1][j - 1 * w[i]] + 1 * v[i]
选择 2 件物品 i 的最大价值 = dp[i-1][j - 2 * w[i]] + 2 * v[i]
...
选择 k 件物品 i 的最大价值 = dp[i-1][j - k * w[i]] + k * v[i], k * w[i] <= j

但这样会引入第三维k的遍历,使用斜率优化将其裂项相消
avatar

画图:dp[i][j]只依赖于上一行(i-1)的当前列j,和本行(i)的j左侧某列j-w[i]

则压缩成1D时,需要从左向右正序遍历列j,以确保计算dp[i][j]时利用到了本行左侧已算出的j-w[i],而不是还未算出的默认值:

dp[j] = max(dp[j], dp[j - w[i]] + v[i])

多重背包

视角1:即完全背包,每个物品i能取的次数, 限制为数组m[i]次

for(物品i顺)
    for(体积j逆)
        for (物品i得选择个数k = 0 -> m[i]) {
            dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i])
}

视角2:即01背包,第i个物品重复了m[i]次,每个重复物品只能用一次
💡快速幂思想:不用平均拆成m[i]份,按照二进制拆成1,2,4,...log(m[i])份,每份只用一次,照样能表示出总体积。如果m[i]不是2的幂,拆剩下的单独作为一个物品

    List<newGoods> goods = new ArrayList<>();
    原价值v[],原体积w[],原可用次数m[]
    for(int i = 0; i < n; ++i) {
        for(int k = 1; k <= m[i]; k *= 2) {
            m[i] -= k;
            goods.add(新价值=v[i] * k, 新体积=w[i] * k);
        }
        if (m[i] > 0) goods.add(新价值=v[i] * m[i], 新体积=w[i] * m[i]);
    }

    // 01背包...

分组背包

有N件物品和一个容量为V的背包。第i件物品的费用是Ci,价值是Wi。这些物品被划分为K组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。组内枚举即可。

    for(int i = 0; i < n; ++i) {
        int K = 组内物品个数
        for(int j = m; j >= v[i]; --j) {
            for k 0 -> K
                dp[j] = max(dp[j], dp[j - w[0~k]] + v[0~k])]);
        }
    }
posted @ 2022-08-31 11:39  Blazer96  阅读(25)  评论(0)    收藏  举报