Files
leetcode-master/problems/背包问题理论基础.md
youngyangyang04 f0ada5141c Update
2020-12-26 18:55:25 +08:00

3.7 KiB
Raw Blame History

背包问题

背包九讲其实看起来还是有点费劲的,而且都是伪代码理解起来吃力

完全背包

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

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

关于完全背包和01背包的差别还是好好好讲一讲。

同样因为leetcode上没有纯完全背包问题都是需要完全背包的各种应用需要转化成完全背包问题所以我这里还是以纯完全背包问题进行讲解理论和原理。讲leetcode题目的时候则侧重于讲如何转化为完全背包问题。

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

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    }
}

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

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

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    }
}

在讲解01背包中已经讲解了为什么正序遍历物品就可以添加多次。

相信很多同学看网上的文章,关于完全背包介绍基本就到为止了。

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

这个问题很多题解都避而不谈,大家都默认 遍历物品在外层,遍历背包容量在内层,好像本应该如此一样,那么为什么呢?

难道就不能,遍历背包容量在外层,遍历物品在内层?

接下来我们好好分析一下,让大家学个通透!

把对n的历遍放到第一层循环这样才能避免把[1,5]、[5,1]算作两条路径。因为你限制了1,5的顺序

到了i=5之后不可能在发生5,1的情况产生。

对于方式二把对n的历遍放在第二层对于任意的一个状态v,都可能历遍每一种硬币,会导致重复冗余的问题。

如果想加深理解,建议最好把两种方式都实现一下,单步执行查看

背包问题技巧: 1.如果是0-1背包即数组中的元素不可重复使用nums放在外循环target在内循环且内循环倒序 for num in nums: for i in range(target, nums-1, -1): 2.如果是完全背包即数组中的元素可重复使用nums放在外循环target在内循环。且内循环正序。 for num in nums: for i in range(nums, target+1): 3.如果组合问题需考虑元素之间的顺序需将target放在外循环将nums放在内循环。 for i in range(1, target+1): for num in nums:

值得一提的是上面的伪代码中两层for循环的次序可以颠倒。这个结论有可能会带来算法时间常数上的优化。(可能说的就是组合或者排列了)

多重背包

有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用每件耗费 的空间是Ci 价值是Wi 。求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量,且价值总和最大。

这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略 微一改即可。

与 0-1 背包的区别在于每种物品 y 有 k 个,而非 1 个

力扣上面没有多重背包的题目。

总结

总结

后台回复:背包九讲 就可以获得pdf