背包问题是一个组合优化的问题,描述如下:给一个固定大小,能够携重W的背包以及一组有价值重量的物品,找出一个最佳的方案,使得装入包中的物品重量不超过W且总价值最大。
1、问题分析
数据:物品个数n=5,物品重量weights=[2,2,6,5,4],物品价值values=[6,3,5,4,6],背包总容量W=10。
我们设置一个矩阵f来记录结果:f(i.j)可表示可选的物品为{i.....n},背包容量为j(0<j<=w) 时,背包中所放物品的最大价值;
我们先看第一行,物品0的重量为2,价值为6,当容量为0时,什么也放不下,因此第一个格子只能填0,程序表示f(0,0)=0;当j=1时,依然放不下物品0,所以依旧为0,即f(0,1)=0;当j=2时,能放下物品0,则表示为f(0,2)=6,当j=3时,依旧能放下,则表示为f(0,3)=6,依次内推可得第一行的结果:
根据第一行,可以得到以下方程:
重量 | 价值 | 物品id | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
2 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
2 | 3 | 1 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
6 | 5 | 2 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 11 | 11 | 14 |
5 | 4 | 3 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 10 | 11 | 13 | 14 |
4 | 6 | 4 | 0 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
再看第二行,当j=0时,依旧什么都放不下,第一个格子填0,当j=1时,依旧如此,当j=2时,可以选择放不放如物品1:
如果不放入物品1,则背包中有物品0,最大价值为6,即f(0,j)
如果放入物品1,要算出背包放入1后还有多少的容量,然后再根据容量查出他的价值,再加上物品1的价值,即表示为
f(0,j-w(1))+v(1),放与不放要通过比较来决定:
显然此处填6,当j=3时,情况相同,当j=4时,能同时放下,f(1,4)=9,当j>4时,背包只能放入物品0和1,所以最大价值为9;
再看第三行,当j=0、1,什么都放不下,当j=2时,虽然放不下物品2,但是表中可看出,背包的最大容量为6,当j=8时,背包可以放物品0、1,也可以物品0、2,和1、2,物品0、1的价值,可以看到为9,最优值为0/2,总价值为11,当j=10时,三个物品能装下,总价值为14;
整理方程可得到第1行和第2行的使用方程:
根据方程,计算各列得出结果,根还有0-1背包问题的最优方案,得到方程式:
代码如下:
function knapsack(weights, values, w){
var n = weights.length -1;//获取物品个数
var f=[[]];//定义f的矩阵
for(var j=0;j<=w;j++){
if(j<weights[0]){//容量当不下物品0的重量,价值为0
f[0][j]=0;
}else{
f[0][j]=values[0];//否则容量为物品0的价值
}
}
for(var j=0;j<=w;j++){
for(var i=1;i<=n;i++){
if(!f[i]){//创建新的一行
f[i]=[];
}
if(j<weights[i]){//等于之前的最优值
f[i][j]=f[i-1][j];
}else{
f[i][j]=Math.max(f[i-1][j],f[i-1][j-weights[i]]+values[i]);
}
}
}
return f[n][w];
}