深入剖析多重背包问题(下篇)
前言
在前面的三篇文章当中,我们已经仔细的讨论了01背包问题和完全背包问题以及多重背包上篇,在本篇文章当中主要给大家介绍多重背包问题的一种优化方法——二进制优化多重背包,如果你还没有看过多重背包上篇,你需要先阅读多重背包上篇。
多重背包问题介绍
有
种物品和一个容量是 的背包。第 种物品最多有 件,每件体积是 ,价值是 。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 注意:上面使用到的字符含义在本篇文章当中都一样。
多重背包问题跟01背包和完全背包的区别都是在物品的可用次数上,01背包只能使用一次,多重背包可用使用无数次,而多重背包可用使用多次。
多重背包的二进制优化
二进制优化的实现
在正式分析多重背包的二进制优化之前,我们先分析一下多重背包的时间复杂度,假设我们有
在多重背包上篇上篇当中我们提到了多重背包的动态转移方程(
从上面的公式我们可以知道,对于某个有
根据上面分析我们可以知道多重背包能够转化成01背包的原因就是多重背包在转化为01背包之后,01背包能够有多重背包选1个,选2个,选3个,...,选
而我们的二进制优化也主要集中在这个地方。多重背包的二进制优化也是将多重背包问题转化成01背包问题,但是不是将
Scanner scanner = new Scanner(System.in); int N = scanner.nextInt(); int V = scanner.nextInt(); ArrayList v = new ArrayList<>(); ArrayList w = new ArrayList<>(); for (int i = 0; i < N; i++) { // 这个表示第 i 个物品的体积 int vi = scanner.nextInt(); // 这个表示第 i 个物品的价值 int wi = scanner.nextInt(); // 这个表示第 i 个物品有多少个 int si = scanner.nextInt(); // 这段代码主要是实现多重背包能够选择1个 // 选择2个,...,选择S个的效果 for (int j = 1; j <= si; j *= 2) { si -= j ; v.add(vi * j); w.add(wi * j); } if (si > 0) { v.add(vi * si); w.add(wi * si); } }
我们举一个例子来分析上面的代码,假设我们加入一个物品
上面的例子将9个
- 一个
:相当于 。 - 两个
:相当于 。 - 三个
:相当于 ,也就是在动态选择的时候选择了 和 两个物品。 - 四个
:相当于 。 - 五个
:相当于 ,也就是在动态选择的时候选择了 和 两个物品。 - 六个
:相当于 ,也就是在动态选择的时候选择了 和 两个物品。 - 七个
:相当于 ,也就是在动态选择的时候选择了 、 和 三个物品。 - 八个
:相当于 ,也就是在动态选择的时候选择了 、 和 三个物品。 - 九个
:相当于 ,也就是在动态选择的时候选择了 、 、 和 四个物品。
相信经过上面的例子之后你已经大致明白了二进制优化的大致实现过程,二进制优化也是将多重背包转化成01背包但是和之前的转化不同的是,我们不是将a = 9 - 1 - 2 - 4
。这样的划分我们可以知道,我们划分之后的物品的数目会少非常多。如果物品的次数的最大值是int
类型的最大值,如果我们一个一个的划分最多可以划分超过20亿个物品,而上面的划分方式,我们划分出来的物品不会超过32个,因此大大降低了时间复杂度。
之前一个一个的划分我们的时间复杂度为
上面就是我们使用二进制优化方式将多重背包转化成01背包的方式,完整代码如下(下方代码使用了单行数组优化):
import java.util.ArrayList; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int N = scanner.nextInt(); int V = scanner.nextInt(); ArrayList v = new ArrayList<>(); ArrayList w = new ArrayList<>(); for (int i = 0; i < N; i++) { int vi = scanner.nextInt(); int wi = scanner.nextInt(); int si = scanner.nextInt(); for (int j = 1; j <= si; j *= 2) { si -= j ; v.add(vi * j); w.add(wi * j); } if (si > 0) { v.add(vi * si); w.add(wi * si); } System.out.println(v); System.out.println(w); } int[] f = new int[V + 1]; for (int i = 0; i < v.size(); i++) { for (int j = V; j >= v.get(i); j--) { f[j] = Math.max(f[j], f[j - v.get(i)] + w.get(i)); } } System.out.println(f[V]); } }
折叠
二进制优化的本质
我们知道任何一个数都有他的二进制形式,任何一个数都可以由2的整数次幂相加得到:
假如我们有15个物品
因为我们最终可能
总结
本篇文章主要给大家介绍的多重背包问题的二进制优化,里面的逻辑还是稍微有点复杂的,可能需要大家仔细去体会,大家在看文字的时候可以参考代码仔细分析,可以理解的更好一点。
以上就是本篇文章的所有内容了,希望大家有所收获,我是LeHung,我们下期再见!!!(记得点赞收藏哦!)
更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore
关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。