知识总结:简单的背包DP模型 | 豆包MarsCode AI刷题

89 阅读5分钟

动态规划中的背包问题是一种经典模型,主要涉及如何在有限的容量下选择物品,使得总价值最大化。以下是几种常见的简单类型

个人学习建议

先理解DP这种利用问题的最优子结构实现的巧妙解法。不妨自己模拟DP的计算过程,自己手算几题基本就了解了。 然后就是大量刷题咯,但是也不能一直刷水题,要刷难度略高于自身的题目。动态规划的模型十分灵活,变种繁多,但是只要我们认真分析,从几个基本模型出发,都能解决。

1. 0/1 背包问题

问题描述

给定 n 个物品,每个物品有一个重量 w[i] 和一个价值 v[i]。你有一个容量为 W 的背包。你需要选择一些物品放入背包,使得总重量不超过 W,并且总价值最大化。每个物品只能选择一次(0 或 1 次)。

解法以及时空复杂度分析

  • 状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]),其中 dp[i][j] 表示前 i 个物品在容量为 j 时的最大价值。
  • 考虑第i个物品放或者不放,放或不放,分别从两个容量不同的背包继承来
  • 时间复杂度是N*V无疑了,空间却可以优化为W。我们可以去掉第一维,相当于滚动数组优化,如果我们背包容量的循环采用V...0的顺序倒推的话,那么能保证求dp[v]时,dp[v-c[i]]的状态实际是dp[i-1][v-c[i]]的值了。
  • 初始化细节,如果要求恰好装满背包,那么初始化dp[0]=0,其他的设为负无穷即可。如果不要求装满,只希望价值最大,那么全清0即可。可以这样理解,初始化的dp数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值0的东西恰好装满,其他容量均没有合法解,属于未定义状态,那么值就是负无穷,如果背包不一定要装满,那么任何容量背包都有一个合法解"什么都不装",价值为0。

2. 完全背包问题

问题描述

与 0/1 背包问题类似,但每个物品可以选择无限次。这就不是放或者不放的问题了,而是放多少的问题了

解法以及时空复杂度分析

  • 如果考虑加一重循环来考虑放几件的话,或者将一种物品拆分为多件物品,还可以使用二进制拆分方法,将一种物品拆分成多个物品集合,每个集合都是2的次幂个相同物品的捆绑包,只要这个捆绑包的体积不超过V即可,这样这些捆绑包就能组合出不同件数的物品了,但时间复杂度都会超过W*N。
  • 完全背包有一个简单的优化,如果一件物品的体积大还价值小,明显可以去掉它,N^2解决。对于背包类问题,还可以先去掉大于背包体积的单件物品,然后计算出费用相同的物品中价值最高的是哪个,V+N解决。
  • 如果把01背包的倒序计算改为顺序的话,那么就是完全背包了,在第i件状态时,容量W的状态也会考虑到已经选了第i件物品的W-c[i]的状态,变成了加选一件,这也是利用了局部最优子结构的性质重复了之前的枚举过程,优化了枚举的复杂度
  • 状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i]),其中 dp[i][j] 表示前 i 个物品在容量为 j 时的最大价值。

3. 多重背包问题

问题描述

与 0/1 背包问题类似,但每个物品有一个数量限制 c[i],表示最多可以选择 c[i] 个。每种物品可以选择有限次

解法以及时空复杂度分析

  • 和上面的完全背包类似,可以直接简单的加一重循环来实现。
  • 但是还有一种难度不高,效率也较快的解法:每种物品的数量不一样多为c[i],简单的思路就是拆分,二进制拆分1,2,4...2(k-1)加上c[i]-2k+1(k是满足这项>0的最大整数),这样就能保证能组合出0...2k-1件,2k...c[i]件的物品选择。
  • 此外还有V*N的更优解法,是用单调队列优化,难度较大,大家可以自行查阅

总结

背包问题的核心在于如何选择物品以最大化价值,同时满足容量限制。不同类型的背包问题在状态转移方程和物品选择策略上有所不同,但都可以通过动态规划来解决。理解这些基本类型有助于解决更复杂的背包问题。