引言
完全背包和01背包最大的区别就是:完全背包中装背包的物品可以重复使用多次,
这里还是拿经典的背包问题论述;
问题:有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
这就是完全背包问题,但是其实和01背包就区别在每种物品有无限件。其他几乎一模一样,
那么这道题该怎么解决呢?
步骤和01背包一样,这里只说区分点:
不同之处就在于循环顺序上,
01背包内部的循环是从大到小倒序遍历,为了保证每个物品仅被添加一次。
代码如下:
for(int i = 0; i < weight.size(); i++) { // 遍历每一件物品
for(int j = bagsize; j >= weight[i]; j--) { // 遍历不同的背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
因为完全背包每个物品可以添加多次,所以内部循环就不能从大到小遍历了,正序遍历就可以了,否则就不符合题意了
代码如下:
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]);
}
}
当然这里同样会出现一个问题,这个循环内外两层可以交换位置吗?
在这个完全背包问题中,不论怎么交换位置,对结果都是没有影响的,
但是,因为这是纯粹的完全背包问题,所以没有影响,
如果问装满背包有几种方式的话?那么两个for循环的先后顺序就有很大区别了;
求方式数就是求组合数,而有的题目会让我们求排序数,一定要注意这两者的区分,
组合不强调元素之间的顺序,排列强调元素之间的顺序
就比如:1 1 2 3 和 1 2 3 1
这是一种组合,因为里面元素都是一样的
这是两种排序,因为里面元素顺序都不同
那么求组合数和排列数的遍历顺序又是怎样呢?
只需要记住下面两点:
如果求组合数就是外层for循环遍历物品,内层for遍历背包容量。
如果求排列数就是外层for遍历背包容量,内层for循环遍历物品。
这里有两道很简单的题目可以感受一下这两种问题的区别:
求组合数:零钱兑换II
求排列数:爬楼梯
总结
其实完全背包问题也就几点注意,整理一下:
1,背包容量的循环是正序的,因为一个物品要加很多次
2,纯完全背包问题:物体和背包容量的内外循环顺序可以随意改变
3,求组和数类问题:外循环是物品,内循环是背包容量(保证每个物品在不同容量背包中出现次序只为一次)
4,求排列数类问题:外循环是背包容量,内循环是物品(每个背包容量下将各种情况都概括)
一定要清楚排列和组合的区别
同时在这里也把01背包规整一下做个对比
1,背包容量的循环是倒序的,因为一个物品只能加一次
2,如果dp是二维定义的(优化前),则内外循环顺序没有关系,都可以
3,如果dp是一维定义的(优化后),则需要注意:
外循环是物品,内循环是背包容量,且内外循环能不能交换位置;
这些都是要注意的地方,在做题或者面试时都有可能遇到,所以还是需要理解掌握;