背包问题解题方法总结

最近在牛客刷题遇到好几道背包问题,索性这两天集中火力刷了一些这类的题。这里总结一下0-1背包完全背包多重背包三种基本的背包问题的解题套路。(均基于动态规划的思想)

0-1背包

题目:有 N 件物品和容量为 W 的背包。第 i 件物品的重量为 w_i,价值为 v_i,求将不超过背包容量的物品装入背包能得到的最大价值。

特点,每件物品的数量只有一个,可以选择放或不放某件物品。

dp[i][j]表示将前 i+1 件总重量不超过 j 的物品放入背包能获得的最大价值,则可以用以下的转移方程来表示这个过程:

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

注意到dp数组第i行的值更新只跟 i-1 行有关,因此可以通过滚动数组或者反向更新的方式优化一下空间复杂度,在动态规划解题的时候这是一种常用的空间复杂度优化方式。优化后的代码如下:

for(int i = 0; i < N; i++){
    // 注意到这里dp需要从后往前更新,避免更新前就把旧值覆盖
    // 从实际意义上来说,因为每件物品只有一个,从后向前更新保证了更新是在还没放入过当前物品的前提下进行的
    for(int j = W; j >= w[i]; j--){  
        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
    }
}

完全背包

题目:有 N 种物品和容量为 W 的背包。第 i 种物品的重量为 w_i,价值为 v_i,每种物品的数量无限。求将不超过背包容量的物品装入背包能得到的最大价值。

特点:每种物品的数量无限多。

考虑到每种物品的数量无限。用 dp[j] 表示在重量不超过 j 的情况下背包中物品可以达到的最大价值,则转移方程如下:

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

核心代码如下:

for(int i = 0; i < N; i++){
    for(int j = w[i]; j <= W; j++){    // 这里和0-1背包不同
        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
    }
}

注意内层for循环是从前向后更新dp数组的,这是唯一和上面的0-1背包问题区别的地方。原因在于,题目中每种物品的数量无限多,在放入一件物品 i 时,要考虑之前已经放过物品 i 的情况。

多重背包

题目:有 N 种物品和容量为 W 的背包。第 i 种物品的数量有n_i个,每个物品重量为 w_i,价值为 v_i,每种物品的数量无限。求将不超过背包容量的物品装入背包能得到的最大价值。

特点:每种物品的数量不止一个,但有限。

基本的多重背包问题状态转移方程如下:

\[dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]), 0\leq k \leq n[i] \]

核心代码如下:

for(int i = 0; i < N; i++){
    for(int j = W; j >= w[i]; j--){  
        for(int k = 1; k <= n[i]; k++){
            if(j < k * w[i])  break; 
            dp[j] = Math.max(dp[j], dp[j - w[i] * k] + v[i] * k);
        }
    }
}

一些背包问题的题目

上面讨论的三种情况只是最基本的背包问题,实际刷题过程中会遇到这些基本问题的变体,例如需要背包正好装满、求最小的物品件数、求装包的方案数等等。这里整理一些题目,后面遇到了持续更新~

0-1背包类题目

考试策略
篮球队 - 求方案数
牛妹的春游 - 最小花费,多个条件,初始值限制
服务部署 - 多重条件

完全背包类题目

换零钱 - 求总方案数
最少数量货物装箱问题 - 物品件数最小,恰好装满
拼凑面额 - 求方案数

多重背包类题目

乔乔的包

posted @ 2020-07-20 21:51  旺仔真知棒  阅读(1232)  评论(0编辑  收藏  举报