最基本的背包问题就是01背包问题(01 knapsack problem):一共有N件物品,第i(i从1开始)件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?
背包问题(Knapsack problem)是一种组合优化的NP完全(NP-Complete,NPC)问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。NPC问题是没有多项式时间复杂度的解法的,但是利用动态规划,我们可以以伪多项式时间复杂度求解背包问题。
01背包:每个物品只可以使用一次,有且只有两种情况,要么放入背包,要么不放。
完全背包:商品数量是无限制的,可以向包中放入相同的多个物品。
多重背包:不同的物品,数量是不同的。(01 + 完全背包 的 结合体)。
通过上面对背包问题的理解之后,发现背包问题的核心就是:
而要满足这两个核心的业务,采用回溯算法(组合问题)加上一些额外的校验就可以解决这个问题了,而回溯算法其实就是暴力搜索。
假设有m个物品,先找出这m个物品所有可能的组合,然后再对这些组合判断是否能放入背包以及是否是最大价值。比如有A, B, C三个物品,所有可能的组合是A, B, C, AB, AC, BC, ABC。这其实就是数学中的组合问题了,所以首先要解决的就是如何求出m个物品的所有组合。
假设我们想在长度为n的字符串中求m个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:
public class packagePro {
public static void main(String[] args) {
//初始化的数据
char num[] = new char[]{'a', 'b', 'c', 'd'};
//存储选出来的数据
List stack = new ArrayList<>();
List> res = new ArrayList<>();
//因为要 挨个取出来,做出是否放入背包的校验
for (int i = 1; i <= num.length; i++) {
combine(num, 0, i, stack, res);
}
for (List characters : res) {
System.out.println(characters.toString());
}
}
/**
* 从一个n个元素的数组中取出m个元素,有多少种取法
* 从字符数组chs 中第begin个字符开始挑选number个字符加入stack中
*
* @param chs 字符数组
* @param begin 第begin个字符开始
* @param number 挑选number
* @param stack
*/
public static void combine(char[] chs, int begin, int number, List stack, List> res) {
//不挑选,结束
if (number == 0) {
res.add(new ArrayList(stack));
return;
}
//开始指针,走到末尾,结束
if (begin == chs.length) {
return;
}
//放入背包
stack.add(chs[begin]);
combine(chs, begin + 1, number - 1, stack, res);
//不放入背包
stack.remove(stack.size() - 1);
combine(chs, begin + 1, number, stack, res);
}
}
背包容量:4
3样物品:A(30元,4千克)、B(20元,3千克)、C(15元,1千克)。
通过上面的组合代码实现,发现回溯的核心问题就在于是否放入背包的选择,那么我们在放入的同时计算价值,最终产生如下代码。
public class bag01 {
//背包最大数量
static int bagMax = 4;
//物品价值
static int values[] = {30, 20, 15};
//物品重量
static int weights[] = {4, 3, 1};
//物品数量
static int N = weights.length;
public static void main(String[] args) {
System.out.println("最终背包中的最大价值为:"+bagProblem(N - 1, bagMax));
}
/**
* @param i 处理到第i件物品
* @param bagMax 剩余的空间为
* @return 最大价值是多少
*/
public static int bagProblem(int i, int bagMax) {
int maxValue = 0;
//如果没有东西可以放入
if (i == -1) {
return 0;
}
//如果剩余空间大于所放的物品
if (bagMax >= weights[i]) {
//放第i件
int value1 = bagProblem(i - 1, bagMax - weights[i]) + values[i];
//不放第i件
int value2 = bagProblem(i - 1, bagMax);
maxValue = Math.max(value1, value2);
}
return maxValue;
}
}
上述代码执行结果为:
动态规划-加载中......