动态规划 (三) 背包问题

来源:互联网 发布:阴阳师6星土豪酒吞数据 编辑:程序博客网 时间:2024/06/05 17:19

问题描述


有n个物品,且每个物品都只有一个。物品i的体积为v[ i ],重量为w[ i ]。背包的体积为C,求怎样装载能使得背包里装的物品最重?

思路分析


之所以叫0-1背包问题,是因为所有的物品都只有一个,在选择物品装载到背包里的时候,只有两种选择:不装载或装载,对应计算机里的0(FALSE)和1(TRUE)的概念。我们用 dp[ i ][ j ] 表示“把前 i 个物品装到体积为 j 的背包中的最大总重量”。得状态转移方程:
dp[i][j] = max(dp[i-1][j], dp[i-1][j - v[i]] + w[i]);
其中,dp[ i - 1 ][ j ] 表示不把物品 i 装载进背包,所以背包的重量是前面 i-1 个的重量;后一项 dp[ i - 1][ j - v[ i ] ] + w[ i ] 表示若把第 i 个物品装载进背包,则必须由最优子问题得到,所以没装载 i 之前重量一定也是最大的。

我们发现循环的时候,这个转移方程是一个二维数组,且第 i 行取决于第 i -1行的值,因此初始化时,只要将第 0 行全部置为 0 即可,即 dp[ 0 ] [ j ] = 0;

代码示例

for(int j = 0; j <= C; ++ j) {dp[0][j] = 0;}for(int i = 1; i <= n; ++ i) {scanf("%d %d", &v, &w);for (int j = 0; j <= C; ++ j) {if(j >= v) {dp[i][j] = max(dp[i-1][j], dp[i-1][j - v] + w);} else {dp[i][j] = dp[i-1][j];}}}

dp[ i ][ j ] 表示“把前 i 个物品装到体积为 j 的背包中的最大总重量”。所以外循环 i 表示从前1个物品到前n个物品,内循环 j 表示体积从 0 到 最大的 C。由于数据是一样样计算的,所以可以不用提前把所有的数据都读好,也不用存起来。

滚动数组

更奇妙的是,我们可以把数组dp编程一维的,这里非常的精妙和巧合,但是要注意这只是程序代码的优化,并不是说 0 - 1 背包问题就是一维背包问题。

memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; ++ i) {scanf("%d%d", &v, &w);for(int j = C; j >= v; j --) {dp[j] = max(dp[j], dp[j-v] + w);}}

考虑为采用滚动数组的情况,f[ i ][ j ]的值要么是f[ i - 1][ j ],要么是f[ i - 1][ j  - v ] + w;由于j是从右往左计算的,所以现在f[j]里保存的是f[ i - 1 ] [ j ],而f[j - v]里保存的是f[ i - 1 ] [ j - v ]的值,所以正好可以。这样子的话,我们可以节省了不少空间的代价。 
注意内循环 j 是到 v 即停止了的,这样子就不用判断 j 和 v 的关系了,因为 j >= v 始终成立。若是 j  < v 的情况,f[ j ] 中本来就保存了 f[ i - 1][ j ]的信息,也不用更新。

若背包必须装满

此外,若题目要求,背包必须装满,则只需要改一下初始化的条件就行。
memset(dp, -0x3f3f, sizeof(dp));dp[0] = 0;for(int i = 1; i <= n; ++ i) {scanf("%d%d", &v, &w);for(int j = C; j >= v; -- j) {dp[j] = max(dp[j], dp[j-v] + w);}}

背包九讲里是这样子解释的:初始化的过程,实际就是说,当一件物品都没有时的解。所以若要求必须装满背包,只有一种解,就是dp[0],即让价值为0的物品,恰好装进容量为0的包里。其他的都是非法解,所以初始化为负∞才行。而没有要求一定要装满背包的情况下,则所有的解都可以是正确的解,所以可以初始化为0;

可重复背包问题

可重复背包是说,每种物品的个数是无限的,而不是0-1背包中,物品只有一个。相似的,有状态转移方程:
dp[i][v]=max{ dp[i-1][v-k*c[i]]+k*w[i] | 0<=k*c[i]<=v }
就是说,这个物品可以选 0 次,1 次,2次... v / c[i] 次,知道装不下为止。

但是也有O(NV)复杂度的优化,而且代码很简单,就是上面的滚动数组实现0-1背包,内循环 j 倒过来就行了。原理不明,可能是我水平太低了,目前无法理解。
伪代码如下:
procedure CompletePack(cost,weight)    for v=cost..V        f[v]=max{f[v],f[v-c[i]]+w[i]}



参考资料

  • 《算法竞赛入门经典》 & 《训练手册》 刘汝佳
  • 背包九讲
  • 刷题的博文
0 0
原创粉丝点击