动态规划:完全背包问题

本题力扣上没有,是刷的卡码网第52题52. 携带研究材料感兴趣的小伙伴可以去刷一下,是ACM模式。

题目:

题目描述:

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。

小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料可以选择无数次,并且可以重复选择。

输入描述:

第一行包含两个整数,N,V,分别表示研究材料的种类和行李空间

接下来包含 N 行,每行两个整数 wi 和 vi,代表第 i 种研究材料的重量和价值

输出描述:

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

输入示例:

4 5
1 2
2 4
3 4
4 5

输出示例:

10

提示信息:

第一种材料选择五次,可以达到最大值。

数据范围:

1 <= N <= 10000;
1 <= V <= 10000;
1 <= wi, vi <= 10^9.

思路:

完全背包

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。

在下面的讲解中,依然举这个例子:

背包最大重量为4。

物品为:

重量 价值
物品0 1 15
物品1 3 20
物品2 4 30

每件商品都有无限个!

问背包能背的物品最大价值是多少?

01背包和完全背包唯一不同就是体现在遍历顺序上,所以本篇博客就不去做动规五部曲了,我们直接针对遍历顺序经行分析!(要先掌握01背包)
01背包问题详细请见动态规划:01背包问题(二)

首先再回顾一下01背包的核心代码:

    for i in range(bag_nums):  # 遍历每个物品
        for j in range(bag_weight, weight[i] - 1, -1):  # 从后往前遍历背包重量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])  # 更新当前背包重量对应的最大价值

我们知道01背包内层循环是从大到小遍历,为了保证每个物品仅被添加一次。

而完全背包的物品是可以添加多次的,所以要从小到大去遍历,即:

for i in range(bag_nums):
    # 从weight[i]到bag_weight遍历,更新dp数组
    for j in range(weight[i], bag_weight + 1):
        # 更新dp[j]的值
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

具体原因01背包中都有讲过

还有一个很重要的问题,为什么遍历物品在外层循环,遍历背包容量在内层循环?

01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。

在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序是无所谓的!

因为dp[j] 是根据 下标j之前所对应的dp[j]计算出来的。 只要保证下标j之前的dp[j]都是经过计算的就可以了。

遍历物品在外层循环,遍历背包容量在内层循环,状态如图:

动态规划:完全背包问题_第1张图片
遍历背包容量在外层循环,遍历物品在内层循环,状态如图:

动态规划:完全背包问题_第2张图片
看了这两个图,大家就会理解,完全背包中,两个for循环的先后循序,都不影响计算dp[j]所需要的值(这个值就是下标j之前所对应的dp[j])。

注意,这里先后循环顺序可以颠倒指的是纯完全背包问题,如果结合具体题目可能就不能颠倒。

代码及详细注释:

# 读取输入
N = [int(x) for x in input().split()]
bag_nums = N[0]  # 物品的数量
bag_weight = N[1]  # 背包的容量
weight = []  # 物品的重量
value = []  # 物品的价值
# 读取每个物品的重量和价值
for _ in range(bag_nums):
    a, b = map(int, input().split())
    weight.append(a)
    value.append(b)
# 创建一个长度为bag_weight+1的数组dp,用于记录背包容量为j时的最大总价值
dp = [0] * (bag_weight + 1)
# 遍历每个物品
for i in range(bag_nums):
    # 从weight[i]到bag_weight遍历,更新dp数组
    for j in range(weight[i], bag_weight + 1):
        # 更新dp[j]的值
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
# 输出背包容量为bag_weight时的最大总价值
print(dp[bag_weight])

你可能感兴趣的:(算法,python,动态规划,算法,leetcode,python)