24.简单背包问题

一、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 i1 个物品的总体积有关系,和具体的策略没有关系,前 i − 1 i-1 i1 个物品在相同体积下的价值越大越好

  • 而对于第 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[i1][j],dp[i1][jw[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[i1][jw[i]]

  • 可以发现它并不会影响比它体积更大的部分,而且只与 i − 1 i-1 i1 的情况有关

  • 所以我们可以从大到小枚举体积,而不是从小到大,则可以省去第一维。因为我们总是用小体积的部分去更新大体积的部分,这样小体积的部分保存的值自然就是前 i − 1 i-1 i1 个物品的内容了

  • 我们可以化简状态转移方程:
    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[jw[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[i1][j],dp[i1][jkw[i]]+kv[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(Vti),空间复杂度 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(Vlog 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[i1][jkw[i]]+kv[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[i1][j],dp[i1][jw[i]]+v[i],dp[i1][j2w[i]]+2v[i],...)

  • 再来看看 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][jw[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][jw[i]]=max(dp[i1][jw[i]],dp[i1][j2w[i]]+v[i],...)

  • 结合我们上文的代码不难发现, d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][jw[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][jw[i]] 的计算仅有一点点细微的差异,所以完全可以直接用 d p [ i ] [ j − w [ i ] ] dp[i][j-w[i]] dp[i][jw[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背包问题

P1048 [NOIP2005 普及组] 采药

P2340 [USACO03FALL]Cow Exhibition G

2.多重背包问题

P2347 [NOIP1996 提高组] 砝码称重

P6771 [USACO05MAR] Space Elevator 太空电梯

3.完全背包问题

P1616 疯狂的采药

P2722 [USACO3.1] 总分 Score Inflation

你可能感兴趣的:(算法竞赛讲义,动态规划,算法,背包问题)