夕拾算法进阶篇:17)01背包和完全背包问题 (动态规划DP)

01背包

问题描述:

有n件物品,每件物品重量为w[i],价值为c[i],现有一个容量为V的背包,问如何选取物品放入背包,使得背包内的总价值最大?

问题分析:

令dp[i][v]表示前i件物品(0<=i<=n,0<v<V)恰好装入容量为v的背包中所能获得的最大价值,下面考虑如何求dp[i][v],考虑第i件物品的选择策略,有如下2中策略:

(1)不放第i件物品,那么问题转换为前i-1件物品放入容量为v的背包中所能获得的最大价值,即dp[i-1][v]。

(2)放第i件物品,那么问题转换为前i-1件物品放入容量为v-w[i]的背包中所能获得的最大价值,即dp[i-1][v-w[i]]+c[i]

由此可以得到其状态转移方程:

                                dp[i][v]=max{dp[i-1][v],dp[i-1][v-w[i]]+c[i]}      (1=<i<=n,w[i]<=v<=V)

其边界为dp[0][v]=0(下面的写法得保证dp始数组初始全为0),而最后n件物品放入到容量V中的最大价值即dp[n][V]。其代码实现如下:

for(int i=1;i<n;i++){
	for(int v=w[i];v<=V;v++){ //v从w[i]开始是保证下标v-w[i]>=0
		dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i])
	}
}
其时间和空间算法复杂度皆为O(nV),但空间复杂度还可以优化,考虑如下图:

dp[i][v]只和上一行的蓝色位置相关,也就时说求dp[i][v],只需要上一行的值即可。所以可以使用一维数组保持上一行的dp数组的值。因此其状态转移方程可以改下如下:

                     dp[v]=max(dp[v],dp[v-w[i]]+c[i])    (1<=i<=n,w[i]<=v<=V)

需要注意的是v必须从V开始逆序枚举,因为dp[i][v]右边的已经计算出来的部分是留给下一行使用的,而其左上角的阴影部分是当前需要使用的。若从w[i]开始正向枚举,dp[i][v]左边的值已经是给下一行使用的并覆盖了需要计算的值(ps:完全背包巧好是这情况),其代码如下:

for(int i=1;i<n;i++){
	for(int v=V;v>=w[i];v--){
		dp[v]=max(dp[v],dp[v-w[i]]+c[i])
	}
}


完全背包

问题描述:

有n种物品,每件物品重量为w[i],价值为c[i],现有一个容量为V的背包,问如何选取物品放入背包,使得背包内的总价值最大。其中每种物品都是无穷件。

问题分析:

可以看出,完全背包和01背包的唯一的区别在于:01背包的同种物品只有1件,而完全背包可以无限放入。同样,令dp[i][v]表示前i件物品(0<=i<=n,0<v<V)恰好装入容量为v的背包中所能获得的最大价值,其也有2中选择策略:

(1)不放第i件物品,那么问题转换为前i-1件物品放入容量为v的背包中所能获得的最大价值,即dp[i-1][v],这和01背包是一样的。

(2)放第i件物品,这里和01背包略有不同。因为完全背包同种物品可以放多件,因此完全背包选择放第i件物品之后并不是转移到了dp[i-1][v-w[i]],而是转移到了dp[i][v-w[i]],这是因为放了第i件物品之后,还可以继续放第i件物品,直到第二维的v-w[i]无法保持大于等于0为止。

由此可以得到其状态转移方程

                     dp[i][v]=max{dp[i-1][v],dp[i][v-w[i]]+c[i]}      (1=<i<=n,w[i]<=v<=V)

看上去和01背包神似,唯一的区别就在于max的第二个参数是dp[i]还是dp[i-1],而这个状态转移方程同样可以改写成一维形式:

                     dp[v]=max(dp[v],dp[v-w[i]]+c[i])    (1<=i<=n,w[i]<=v<=V)

写成一维后和01背包完全相同,唯一的区别在于这里的v是正向枚举的,如下图:


求解dp[i][v]只需要其正上方和左边的值,如果让v从小到大枚举,dp[i][v-[w[i]]的值在上一行就已经计算出来了,而计算出dp[i][v]之后dp[i-1][v]就在也用不到了,可以直接覆盖。其实现的代码如下

for(int i=1;i<n;i++){
	for(int v=w[i];v<=V;v++){
		dp[v]=max(dp[v],dp[v-w[i]]+c[i])
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
01背包问题一个经典的动态规划问题,它的目是在给定一组物品和一个包容量的情况下,选择一些品放入背包中,使得放入背包的物品总价最大,同时不能超过背包的容量。 动态规划算法是解决01背包问题的常用方法。下面是该算法的实现步骤: 1. 创建一个二维数组dp,其中dp[i][j]表示在前i个物品中选择一些放入容量为j的背包中所能获得的最大价值。 2. 初始化dp数组的第一行和第一列为0,表示背包容量为0或者没有物品可选时,最大价值都为0。 3. 对于每个物品i,遍历背包容量j从1到背包总容量: - 如果当前物品i的重量大于背包容量j,则无法将物品i放入背包中,此时最大价值为dp[i-1][j]。 - 如果当前物品i的重量小于等于背包容量j,则可以选择将物品i放入背包中或者不放入背包中: - 如果选择放入物品i,则最大价值为dp[i-1][j-w[i]] + v[i],其中w[i]表示物品i的重量,v[i]表示物品i的价值。 - 如果选择不放入物品i,则最大价值为dp[i-1][j]。 - 取上述两种情况的最大值作为dp[i][j]的值。 4. 遍历完所有物品后,dp[n][C]即为问题的解,其中n表示物品的个数,C表示背包的总容量。 下面是01背包问题动态规划算法的实现代码: ```python def knapsack(W, wt, val, n): dp = [[0 for _ in range(W+1)] for _ in range(n+1)] for i in range(1, n+1): for j in range(1, W+1): if wt[i-1] > j: dp[i][j] = dp[i-1][j] else: dp[i][j] = max(dp[i-1][j], dp[i-1][j-wt[i-1]] + val[i-1]) return dp[n][W] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值