一、01背包问题
1.题目大意
有 N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。第 i i i 件物品的体积是 w i w_i wi,价值是 v i v_i vi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
2.题目分析
-
因为每个物品只能使用一次,所以每一个物品可以作为一个独立的阶段来看待
-
第 i i i 的物品的选取决策只与前 i − 1 i-1 i−1 个物品的总体积有关系,和具体的策略没有关系,前 i − 1 i-1 i−1 个物品在相同体积下的价值越大越好
-
而对于第 i i i 个物品来说,有选取和不选取两种方式,所以都需要考虑
-
状态转移方程为:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]) dp[i][j]=max(dp[i−1][j],dp[i−1][j−w[i]]+v[i])- d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个物品总体积为 j j j 的最大价值
- w [ i ] w[i] w[i] 表示第 i i i 个物品的体积, v [ i ] v[i] v[i] 表示第 i i i 个物品的价值
-
时间复杂度 O ( N V ) O(NV) O(NV),空间复杂度 O ( N V ) O(NV) O(NV)。
for(ll i=1;i<=N;i++)
{
for(ll j=0;j<=V;j++)
{
if(j>=w[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
else
dp[i][j]=dp[i-1][j];
}
}
3.空间复杂度优化
空间复杂度还可以优化到 O ( V ) O(V) O(V),但时间复杂度已经很难再优化了。
-
计算前 i i i 件物品,总体积为 j j j 的最大价值时,也就是 d p [ i ] [ j ] dp[i][j] dp[i][j],需要的状态是 d p [ i − 1 ] [ j − w [ i ] ] dp[i-1][j-w[i]] dp[i−1][j−w[i]]。
-
可以发现它并不会影响比它体积更大的部分,而且只与 i − 1 i-1 i−1 的情况有关
-
所以我们可以从大到小枚举体积,而不是从小到大,则可以省去第一维。因为我们总是用小体积的部分去更新大体积的部分,这样小体积的部分保存的值自然就是前 i − 1 i-1 i−1 个物品的内容了
-
我们可以化简状态转移方程:
d p [ j ] = m a x ( d p [ j ] , d p [ j − w [ i ] ] + v [ i ] ) dp[j]=max(dp[j],dp[j-w[i]]+v[i]) dp[j]=max(dp[j],dp[j−w[i]]+v[i])- d p [ j ] dp[j] dp[j] 表示总体积为 j j j 的最大价值
- w [ i ] w[i] w[i] 表示第 i i i 个物品的体积, v [ i ] v[i] v[i] 表示第 i i i 个物品的价值
-
时间复杂度 O ( N V ) O(NV) O(NV),空间复杂度 O ( V ) O(V) O(V)。
for(ll i=1;i<=N;i++)
for(ll j=V;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
二、多重背包问题
1.题目大意
有 N N N 件物品和一个容量是 V V V 的背包。每件物品可以使用多次。第 i i i 件物品的体积是 w i w_i wi,价值是 v i v_i vi,使用次数是 t i t_i ti。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
2.题目分析
-
完全背包问题和 01 01 01 背包问题并无本质区别,只是从每个物品只能使用一次变成了可以使用多次
-
可以把 t i t_i ti 个相同的物品当作 t i t_i ti 个不同的物品就可以转化为 01 01 01 背包问题了
-
状态转移方程:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*w[i]]+k*v[i]) dp[i][j]=max(dp[i−1][j],dp[i−1][j−k∗w[i]]+k∗v[i])- d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个物品总体积为 j j j 的最大价值
- w [ i ] w[i] w[i] 表示第 i i i 个物品的体积, v [ i ] v[i] v[i] 表示第 i i i 个物品的价值
- k k k 表示物品的使用次数
-
时间复杂度 O ( V ∑ t i ) O(V\sum{t_i}) O(V∑ti),空间复杂度 O ( N V ) O(NV) O(NV)。
for(ll i=1;i<=N;i++)
for(ll j=0;j<=V;j++)
for(ll k=0;k<=t[i];k++)
if(j>=k*w[i])
dp[i][j]=max(dp[i-1][j-k*w[i]]+k*v[i],dp[i][j]);
- 我们可以利用和 01 01 01 背包问题相同的方式优化多重背包问题的空间复杂度,最终空间复杂度为 O ( V ) O(V) O(V):
for(ll i=1;i<=N;i++)
for(ll j=V;j>=0;j--)
for(ll k=0;k<=t[i];k++)
if(j>=k*w[i])
dp[j]=max(dp[j-k*w[i]]+k*v[i],dp[j]);
3.二进制优化
- 在学习快速幂的时候,我们知道二进制可以用来表示任何数
- 可以将 t i t_i ti 拆分成二进制的形式来枚举物品的数量,而不是直接从 0 0 0 枚举到 t i t_i ti
- 这样的枚举方式相当于最多只有 l o g t i log\ t_i log ti个物品
- 时间复杂度 O ( V ∑ l o g t i ) O(V\sum{log\ t_i}) O(V∑log ti),空间复杂度 O ( N V ) O(NV) O(NV)。
- 其实多重背包问题能够结合单调队列来进行优化,时间复杂度会更低,我们会在后续的课程中再讲到
三、完全背包问题
1.题目大意
有 N N N 件物品和一个容量是 V V V 的背包。每件物品可以任意多次。第 i i i 件物品的体积是 w i w_i wi,价值是 v i v_i vi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
2.题目分析
-
虽然物品有无数个,但是背包的容量依然是有限的
-
可以把完全背包问题看成多重背包问题来求解,进而转化为** 01 01 01背包问题**
-
状态转移方程:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ) dp[i][j]=max(dp[i-1][j-k*w[i]]+k*v[i]) dp[i][j]=max(dp[i−1][j−k∗w[i]]+k∗v[i])- d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个物品总体积为 j j j 的最大价值
- w [ i ] w[i] w[i] 表示第 i i i 个物品的体积, v [ i ] v[i] v[i] 表示第 i i i 个物品的价值
- k k k 表示物品的使用次数
-
时间复杂度 O ( N V 2 ) O(NV^2) O(NV2),空间复杂度 O ( N V ) O(NV) O(NV)。
for(ll i=1;i<=N;i++)
for(ll j=0;j<=V;j++)
for(ll k=0;k*w[i]<=j;k++)
dp[i][j]=max(dp[i-1][j-k*w[i]]+k*v[i],dp[i][j]);
3.优化
-
观察 d p [ i ] [ j ] dp[i][j] dp[i][j] 的状态转移方程,可以展开为:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] , d p [ i − 1 ] [ j − 2 ∗ w [ i ] ] + 2 ∗ v [ i ] , . . . ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i],dp[i-1][j-2*w[i]]+2*v[i],...) dp[i][j]=max(dp[i−1][j],dp[i−1][j−w[i]]+v[i],dp[i−1][j−2∗w[i]]+2∗v[i],...) -
再来看看 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][j−w[i]]的情况:
d p [ i ] [ j − w [ i ] ] = m a x ( d p [ i − 1 ] [ j − w [ i ] ] , d p [ i − 1 ] [ j − 2 ∗ w [ i ] ] + v [ i ] , . . . ) dp[i][j-w[i]]=max(dp[i-1][j-w[i]],dp[i-1][j-2*w[i]]+v[i],...) dp[i][j−w[i]]=max(dp[i−1][j−w[i]],dp[i−1][j−2∗w[i]]+v[i],...) -
结合我们上文的代码不难发现, d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][j−w[i]] 一定能在 d p [ i ] [ j ] dp[i][j] dp[i][j] 之前完成计算
-
d p [ i ] [ j ] dp[i][j] dp[i][j] 和 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][j−w[i]] 的计算仅有一点点细微的差异,所以完全可以直接用 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][j−w[i]] 去计算 d p [ i ] [ j ] dp[i][j] dp[i][j],这样就大大优化了时间复杂度
-
时间复杂度 O ( N V ) O(NV) O(NV),空间复杂度 O ( N V ) O(NV) O(NV)。
for(ll i=1;i<=n;i++)
{
for(ll j=0;j<=V;j++)
{
if(j>=w[i])
dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]);
else
dp[i][j]=dp[i-1][j];
}
}
- 不难发现这个与优化前的 01 01 01背包问题很像,所以我们可以如法炮制优化空间复杂度
for(ll i=1;i<=n;i++)
for(ll j=w[i];j<=V;j++)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
- 最终,完全背包问题的时间复杂度 O ( N V ) O(NV) O(NV),空间复杂度 O ( V ) O(V) O(V)
四、作业
1.01背包问题
P2340 [USACO03FALL]Cow Exhibition G
2.多重背包问题
P6771 [USACO05MAR] Space Elevator 太空电梯