01背包问题
概念
「背包问题」(Knapsack problem)是一种组合优化的NP完全问题。可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。
「01背包问题」是背包问题中的一种。可以描述为:有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。「01背包」的特点就是:每种物品仅有一件,可以选择放或不放。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。
算法
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}方程之中,假设需要放置的是第i件物品,这件物品的体积是c[i],价值是w[i],因此f[i-1][v]代表的就是不将这件物品放入背包,而f[i-1][v-c[i]]+w[i]则是代表将第i件放入背包之后的总价值,比较两者的价值,得出最大的价值存入现在的背包之中。
过程详解
假设有3件物品和一个容量为5的背包。第i件物品的体积是c[i],价值是w[i]。 i表示每件物品下标,j表示容量下标。 「填写第i行时,每行的背包仅能装入<=i的物品」。
建立如下表格分析:
| w | c | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|
| 5 | 2 | ||||||
| 6 | 3 | ||||||
| 7 | 4 |
背包容量为0时,不能装下任何物品,所以对应价值为0,如下表:
| w | c | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|
| 5 | 2 | 0 | |||||
| 6 | 3 | 0 | |||||
| 7 | 4 | 0 |
第一行:
j = 1时,不能装入任何物品,价值为0; j = 2 ~ 5时,可以装入物品1,价值为5。
| w | c | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|
| 5 | 2 | 0 | 0 | 5 | 5 | 5 | 5 |
| 6 | 3 | 0 | |||||
| 7 | 4 | 0 |
第二行:
- j = 1时,不能装入任何物品,价值为0;
- j = 2时,可以装入物品1,价值为5;
- j = 3时,可以装入物品1也可以装入物品2,此时就该考虑是否装入当前物品2。
- 如果装入当前物品,总价值 = 当前物品价值(6) + 剩余容量(当前容量-当前物品容量)所能装入物品的价值(0);
- 否则装入的就是当前容量所能装入上个物品的价值(5);
- 从中选择最大价值(6)。
- j = 4时,同理。此时剩余容量为1,最大价值为6;
- j = 5时,同理。此时剩余容量为2,最大价值为11。
| w | c | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|
| 5 | 2 | 0 | 0 | 5 | 5 | 5 | 5 |
| 6 | 3 | 0 | 0 | 5 | 6 | 6 | 11 |
| 7 | 4 | 0 |
第三行:
- j = 1时,不能装入任何物品,价值为0;
- j = 2时,只能装入物品1,直接复用上次计算结果,价值为5;
- j = 3时,同理,价值为6;
- j = 4时,与第二行j = 3同理,价值为7;
- j = 5时,同理。此时剩余容量为2,所以最大价值为11。
| w | c | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|
| 5 | 2 | 0 | 0 | 5 | 5 | 5 | 5 |
| 6 | 3 | 0 | 0 | 5 | 6 | 6 | 11 |
| 7 | 4 | 0 | 0 | 5 | 6 | 7 | 11 |
代码实现
function knapSack(capacity, weights, values, n){
let a, b, ks = [];
//初始化将用于寻找解决方案的矩阵ks
for( let i = 0; i <= n; i++){
ks[i] = []
}
for(let i = 0; i<=n; i++){
for(let w = 0; w <=capacity; w++){
if( i == 0 || w == 0){ //只处理索引不为0的列和行
ks[i][w] = 0
}else if(weights[i-1] <= w){
a = values[i-1] + ks[i-1][w-weights[i-1]] // 将第i件放入背包之后的总价值
b = ks[i-1][w] // 不将第i件放入背包之后的总价值,取能够存入当前容量的上个物品总价值
ks[i][w] = (a > b)? a : b
}else{
ks[i][w] = ks[i-1][w]
}
}
}
return ks[n][capacity] // 输出背包携带物品价值的最大值
}
let capacity = 5;
let values = [5,6,7];
let weights = [2,3,4]
let n = values.length;
console.log(knapSack(capacity, weights, values, n))