AcWing-完全背包问题(思路+python代码)

1.题目

有 N种物品和一个容量是 V的背包,每种物品都有无限件可用

第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品种数和背包容积。

接下来有 N𝑁 行,每行两个整数 vi,wi用空格隔开,分别表示第 i𝑖种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

10

2.思路 

 对于完全背包问题,我们从题目中可以发现它与01背包问题的巨大不同便是每件物品可以无限使用,但思路还是和解决01背包问题的思路相似,对于01背包问题还不懂的小伙伴可以去看我的01背包问题的讲解:AcWing-01背包问题(思路+python代码)

二维dp数组:

思路和01背包问题一致,均是需要判断第i件物品是放入背包还是不放入背包,但因为完全背包问题中的每件物品都可以无限使用,因此,在完全背包问题中需要考虑是放入多少第i件物品

为了方便理解,我们针对这个题列一个表格

完全背包
编号\体积012345
0000000
10246810
20246810
30246810
40246810

编号:这里的编号是指取几个物品,像当编号为2时,意味着可以取前两个物品

体积:这里的体积是从零到MAX,目的是为了判断当体积为某个值时,取一个物品,两个物品等的价值最大是多少

当体积为0的时候,无论取几个物品,由于总体积为0,无论什么物品均不能放入背包,故它对应的那一列的价值均为0,之后的讲解将不考虑0对应的那一列。

第0行:

因为没有选择任何物品,因此此行全为零

第一行:

编号为1,意味着我们可以取第一个物品,第一个物品的体积为1,当体积为一时,体积等于第一个物品的体积,因此判断是否要放入第一个物品和放入多少个第一件物品,我们假定一个k来表示可以放入多少个第一件物品,k的最大值为当前的v/第i件物品的体积,所以,我们可以算出k的最大值是1/1=1,所以k在此时的最大值是1,倘若不放入第一个物品,它对应的价值是0,而放入第一个物品后,其价值变为2,且此处最多只能放入一个第一个物品,因此,此处填入放入一个物品的价值2,当体积为2时,2>第一个物品的体积1,因此需要接着判断是否放入第一个物品,和放入多少个第一个物品,由前面可知,此时的k=2/1=2,假如不放入第一个物品,其价值为0,放入一个第一个物品,其价值为2,放入两个第一个物品,其价值为4,综合比较,此处填放入两个第一个物品的价值4,我们可以清晰的看到,对于第一行,我们只能取第一个物品,而在体积大于第一个物品的时候,只有k越大,其价值才越大,故体积为3时,k=3时,价值最大,为6,体积为4时,k=4时,价值最大,为8,体积为5时,k=5时,价值最大,为10.

第二行:

当体积为1的时候,体积小于第二个物品的体积,所以,放不下第二个物品,也就是说还是只放入了第一个物品,其价值相当于编号为1,体积为1时的最大价值,也就是2。

当体积为2时,体积等于第二个物品的体积,所以,判断是否放入第二个物品,和放入几个第二个物品,k=2/2=1,因此k的上限为1,倘若不放第二个物品时的最大价值是编号为1,体积为2时的最大价值,也就是4,若放入第二个物品,剩余的体积为2-2=0,故其价值为第二个物品的价值加上上一行体积为零的最大价值(因为已经使用过第二个物品了,且k的上限为1,也就是最多只能放入一个第二件物品,所以以找到上一行,不带第二个物品,体积为零对应的最大值),也就是4+0=4,所以此处填入4。

当体积为3的时候,体积大于第二个物品的体积,判断是否放入第2个物品和放入几个第二件物品k=3/2=1.5,因此,此时的k的上限为1,还是最多只能放入一个第二件物品,当不放入第二件物品的时候的最大价值是编号为1,体积为3对应的最大价值,也就是6,若放入第二件物品,剩余的体积为3-2=1,故其价值为一个第二件物品的价值加上上一行体积为1的最大价值,也就是4+2=6,所以此处填入6。

当体积为4的时候,体积大于第二件物品的体积,判断是否放入第2个物品和放入几个第二件物品,k=4/2=2,因此,此时的k的上限为2,这就意味着最多可以放入两个第二件物品,当不放入第二件物品的时候的最大价值是编号为1,体积为4对应的最大价值,也就是8,若放入一件第二件物品,剩余的体积为4-2=2,故其价值为一个第二件物品的价值加上上一行体积为2的最大价值,也就是4+4=8,若放入两件第二件物品,剩余的体积为4-4=0。故其价值应为两个第二件物品的价值加上上一行体积为零的最大价值,也就是4+4+0=8,对这三个值进行比较,故此处应填入8.

当体积为5的时候,体积大于第二件物品的体积,同理还是一样,判断是否放入第2个物品和放入几个第二件物品,k=5/2=2.5,因此,此时的k的上限为2,最多可以放入两个第二件物品,当不放入第二件物品的时候的最大价值是编号为1,体积为5对应的最大价值,也就是10,若放入一件第二件物品,剩余的体积为5-2=3,故其价值为一个第二件物品的价值加上上一行体积为3的最大价值,也就是4+6=10,若放入两件第二件物品,剩余的体积为5-4=1。故其价值应为两个第二件物品的价值加上上一行体积为零的最大价值,也就是4+4+2=10,对这三个值进行比较,故此处应填入10。

第三行:

同前面推理方式,略

第四行:

同前面推理方式,略

一维dp数组:

与01背包不同,因为完全背包一个物品可以被使用多次,因此它是正序遍历,但方法还是和01背包相似,当背包体积小于第i个物体的体积时,dp[j]=dp[j],因为,假设我要求第三行的dp一维数组,我需要以第二行的dp一维数组为基础进行迭代,因为不放入第三件物品,相当于在相同体积的情况下第二件物品的最大价值,所以dp[j]=dp[j],当背包的体积大于等于第i个物体的体积时,判断不放入该物体,和放入几个第i个物品的价值最大,找到最大价值。

以求第三行dp一维数组为例:

第二行
0246810
第三行
0246810

 

当v=0的时候,无论哪一行都不能放入,故第一列全为0,

当v=1的时候,因为第三件物品的体积为3>1,所以dp[1]=dp[1]=2

当v=2的时候,因为第三件物品的体积为3>2,所以dp[2]=dp[2]=4

当v=3的时候,因为第三件物品的体积为3=3,所以判断是否放入第三件物品和放入几个第三件物品,k=3/3=1,所以最多放入一个第三件物品,dp[3]=dp[3-3]+第三件物品的价值=0+4=4,当不放第三件物品时,dp[3]=6,6>4,所以dp[3]=6

当v=4的时候,因为第三件物品的体积为3<4,所以同理判断是否放入第三件物品和放入几个第三件物品,k=4/3=1.33333....,所以最多放入一个第三件物品,dp[4]=dp[4-3]+第三件物品的价值=2+4=6,当不放第三件物品时,dp[4]=8,8>6,所以dp[4]=8

当v=5的时候,因为第三件物品的体积为3<5,所以判断是否放入第三件物品和放入几个第三件物品,k=5/3=1.666666.....,所以最多放入一个第三件物品,dp[5]=dp[5-3]+第三件物品的价值=4+4=8,当不放第三件物品时,dp[4]=10,10>8,所以dp[4]=10

3.总结

二维数组:

当j>=bag[i-1][0],判断MAX(dp[i-1][j],dp[i-1][j-bag[i-1][0]]+bag[i-1][1])

否则dp[i][j]=dp[i-1][j]

一维数组:

当j>=bag[i-1][0],判断MAX(dp[j],dp[j-bag[i-1][0]]+bag[i-1][1])

否则dp[j]=dp[j]

4.代码

dp列表

[0, 0, 0, 0, 0, 0]
[0, 2, 4, 6, 8, 10]
[0, 2, 4, 6, 8, 10]
[0, 2, 4, 6, 8, 10]
[0, 2, 4, 6, 8, 10]

 我们可以清晰的看到dp列表与我们前面列的表格如出一辙 

python二维dp数组

n,v=map(int,input().split())
bag=[]
for i in range(n):
    bag.append(list(map(int,input().split())))
    #bag.append([int(i) for i in input().split()])列表推导式
dp=[[0 for i in range(v+1)] for j in range(n+1)]
for i in range(1,n+1):
    for j in range(v+1):
        if(j>=bag[i-1][0]):
            dp[i][j]=max(dp[i-1][j],dp[i][j-bag[i-1][0]]+bag[i-1][1])
        else:
            dp[i][j]=dp[i-1][j]
print(dp[-1][-1])

python一维dp数组

n,v=map(int,input().split())
bag=[]
for i in range(n):
    bag.append(list(map(int,input().split())))
    #bag.append([int(i) for i in input().split()])列表推导式
dp=[0 for i in range(v+1)]
for i in range(1,n+1):
    for j in range(1,v+1):
        if(j>=bag[i-1][0]):
            dp[j]=max(dp[j],dp[j-bag[i-1][0]]+bag[i-1][1])
        else:
            dp[j]=dp[j]
print(dp[-1])

  • 31
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
背包问题和0-1背包问题都是动态规划问题,它们的主要区别在于背包问题中的物品可以取任意数量,而0-1背包问题中的物品只能取0或1。 以下是背包问题和0-1背包问题Python代码实现: **背包问题** ```python def knapsack(items, capacity): n = len(items) dp = [ * (capacity + 1) for _ in range(n + 1)] for i in range(1, n + 1): weight, value = items[i - 1] for j in range(capacity, -1, -1): if j >= weight: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j] return dp[n][capacity] ``` 在这个代码中,items是一个列表,包含一系列物品及其重量和价值。capacity是背包的容量。函数返回的是在给定容量下可以获得的最大价值。 **0-1背包问题** ```python def knapsack_0_1(items, capacity): n = len(items) dp = [ * (capacity + 1) for _ in range(n + 1)] for i in range(1, n + 1): weight, value = items[i - 1] for j in range(capacity, -1, -1): if j >= weight: dp[i][j] = max(dp[i - 1][j - weight]) if dp[i - 1][j - weight] else 0 else: dp[i][j] = dp[i][j - weight] if dp[i][j - weight] else float('inf') # 若没有选择的选项,取无穷大(即为取值为零)作为代表。 dp[i][j] = min(dp[i][j], dp[i][j-weight]) if dp[i][j] > capacity else dp[i][j] # 最后如果总的容量大于给定的容量,就不能选之前的选项,那么最后的方案应等于剩余容量的情况下的最优方案。 return dp[n][capacity] # 这个点对应的是选择的最后一个物品时能获得的最大价值。因为是从后向前计算,最后一个物品的价值必定小于其单独的值。如果该物品值过大(超出总容量),就考虑替换之前的选项(且最优的替换方式)而不是完全装入背包中。最后,选取一个方案的最大值就是我们的结果。 ``` 这段代码中的物品同样需要给出其重量和价值,返回的是在给定容量下可以获得的最大价值,但是只能选择0或1,不能选择多个物品。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值