读《背包问题九讲》笔记,大家可以搜一下这份资料,讲的很好,但是只有伪代码,下面贴出我自己用java写的解法
问题:
有N件物品和一个容量为V 的背包。放入第i件物品耗费的费用是Ci,得到
的价值是Wi。求解将哪些物品装入背包可使价值总和最大。
没学过动态规划看到此题大概会想到用穷举法,也就是自底向上的思维,一个个加起来求最优解。但是这种解法十分耗神,我们应该学会用递归的思维(也就是自顶向下)来求此题。
状态转移方程:
F [i, v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
首先贴出O(V*N)解法,这是比较慢的解法。
伪代码为:
import java.util.ArrayList;
//O(V*N)解法
public class bagProblem {
static class Item {//表
int totalValue = 0;//每一项的总价值
ArrayList<Integer> itemLink = new ArrayList<>();//用于记录该项有哪几样物品
}
static ArrayList<Integer> solve(int[] price, int[] weigh, int v) {
int len = price.length;
//创建len+1项,第0项为哨兵,这样做的目的是方便计算
Item[][] table = new Item[len + 1][v + 1];
for (int i = 0; i < len + 1; i++) {//初始化表
for (int j = 0; j < v + 1; j++) {
table[i][j] = new Item();
}
}
for (int i = 1; i <= len; i++) {
for (int j = weigh[i-1]; j <= v; j++) {
Item item1 = table[i - 1][j];//F [i − 1; v]
Item item2 = table[i - 1][j - weigh[i-1]];//F [i − 1; v − Ci]
if (item2.totalValue + price[i - 1] > item1.totalValue) {
table[i][j].totalValue += item2.totalValue + price[i-1];//F [i − 1; v − Ci] + Wi
//将上一项中的物品集合放入到下一项中
table[i][j].itemLink.addAll(item2.itemLink);
table[i][j].itemLink.add(i);
} else {
table[i][j].totalValue = item1.totalValue ;
table[i][j].itemLink.addAll(item1.itemLink);
}
}
}
return table[len][v].itemLink;
}
public static void main(String[] args) {
int[] v = {8, 10, 6, 3, 7, 2};
int[] w = {4, 6, 2, 2, 5, 1};
bagProblem b = new bagProblem();
System.out.println(b.solve(v, w, 12));
}
}
该解法还可以优化为:
下面给出代码:
//O(V)解法
static ArrayList<Integer> solve1(int[] price, int[] weigh, int v) {
int len = price.length;
Item[] table = new Item[v + 1];
for (int i = 0; i <= v; i++) {
table[i] = new Item();
}
for (int i = 0; i < len; i++) {
for (int j = v; j >= weigh[i]; j--) {
int result2 = table[j - weigh[i]].totalValue;
int result1 = table[j].totalValue;
if(result1 < result2 + price[i]){
table[j].totalValue = result2+price[i];
table[j].itemLink.clear();
table[j].itemLink.addAll(table[j - weigh[i]].itemLink);
table[j].itemLink.add(i+1);
}
}
}
return table[v].itemLink;
}