本文主要讲解0-1背包问题的三种解法:
什么是0-1背包问题:
- 给定n个重量为w1,w2,w3…wn,其价值为v1,v2,v3…vn的物品和容量为C的背包,求这个物品中一个最有价值的子集,使得在满足背包的容量的前提下,包内的总价值最大
正文开始
动态规划
- 源码:Backpack_Dynamic 和 测试案例
- 主要思想
- 构造二维矩阵,进行试探性的取值
- 状态转换方程:最优解=Math.max(前一位最优解,当前值+【背包当前重量-物品重量】的价值)
- 主要代码
private int count(Goods[] goods, int packageweight) {
//价值矩阵,列:背包的重量,行:加入的物品
bestvalue = new int[packageweight + 1][goods.length + 1];
//逐层规划,外层循环表示背包重量增加
for (int i = 1; i <= packageweight; i++) {
//内层循环,遍历物品
for (int j = 1; j <= goods.length; j++) {
//如果放入值比背包总重量还大,放弃
if (goods[j - 1].weight > i) {
bestvalue[i][j] = bestvalue[i][j - 1];
} else {
bestvalue[i][j] = Math.max(
bestvalue[i][j - 1],
bestvalue[i - goods[j - 1].weight][j - 1] + goods[j - 1].prices
);
}
}
}
return bestvalue[packageweight][goods.length];
}
- 注意事项
分支限界法(回溯法、剪枝法)
- 源码:Backpack_Branch.java 和 测试案例
- 主要思想
- 通过不断的排列组合,得到最合适的物品装入序列
- 通过背包重量和实际重量进行剪枝操作。
- 主要代码
public void count(int depth) {
if (BackpackRealWeight > BackpackWeight) {
return;
}
BackpackValue = Math.max(BackpackValue, tempValue);
for (int i = depth; i < goods.length; i++) {
tempValue += goods[i].prices;
BackpackRealWeight += goods[i].weight;
//注意此处是i+1,不是depth+1
count(i + 1);
tempValue -= goods[i].prices;
BackpackRealWeight -= goods[i].weight;
}
}
- 注意事项
- 递归的深度,不是depth+1(容易引起死循环),而是当前的循环值。
贪心算法
- 源码:Backpack_Greedy 和 测试案例
- 主要思想
- 将物品按照价值/重量,进行排序,当价值越大,重量越低的时候,物品价值率越高。
- 不断加如价值率最高的物品,直到背包装不下。
- 主要代码
public int Backpack(Integer[] goodsvalue, Integer[] weight, int packageweight) {
if (!check(goodsvalue, weight, packageweight)) {
return -1;
}
BackpackValue = 0;
BackpackRealWeight = packageweight;
goods = getGoods(goodsvalue, weight);
InsertSort.sortMethodT(goods);
for (int i = goodsvalue.length - 1; i >= 0; i--) {
if (packageweight >= goods[i].weight) {
BackpackValue += goods[i].prices;
packageweight -= goods[i].weight;
}
}
BackpackRealWeight = BackpackRealWeight - packageweight;
return BackpackValue;
}
- 注意事项
- 排序算法的选择
- 贪心算法不一定最优,但是比较快