问题描述:
有N件物品和⼀个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是 value[i] 。每件物品只能⽤⼀次,求解将哪些物品装⼊背包⾥物品价值总和最⼤?
例如:
背包最⼤重量为4,物品为:
问背包能背的物品最⼤价值是多少?
思考
- dp[i][j]:表示从下标[0-i]的物品中任意取,放进容量为j的背包中得到的最大价值总和是多少。
2. dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]),dp[i][j]有两种情况推出,dp[i][j]取这两种情况中的最大值,分别是:
- 不放物品i:dp[i][j]=dp[i-1][j]
- 放物品i:dp[i][j]=dp[i-1][j-weight[i]+value[i]];
- 初始化:首先如果背包容量j为0的话,即dp[i][0]=0,⽆论是选取哪些物品,背包价值总和⼀定为0;其次根据递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);i 是由 i-1 推导出来,那么i为0的时候就⼀定要初始化,即初始化dp[0][j]。当j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量⽐编号0的物品重量还⼩。当j >= weight[0]是,dp[0][j] 应该是value[0],因为背包容量放⾜够放编号0物品。
4. 确定遍历顺序:有两个遍历的维度:物品与背包重量。
- 先遍历物品,再遍历背包
- 先遍历背包,再遍历物品
可以看出不管是先遍历物品还是先遍历背包,根本不会影响递推公式的推导。所以两种遍历方式都可以。
- 推导dp数组
代码
var weight=[1,3,4],value=[15,20,30],n=3,m=4;
test_2_wei_bag_problem1(n,m);
function test_2_wei_bag_problem1(n,m){
var dp=new Array(n).fill(0).map(_ =>new Array(m+1).fill(0));
//初始化
for(let i=0;i<n;i++){
for(let j=0;j<=m;j++){
if(weight[0]<=j){
dp[0][j]=value[0];
}
}
}
//两种遍历任选其一即可
//遍历-先遍历背包再遍历物品
for(let i=1;i<n;i++){
for(let j=0;j<=m;j++){
if(weight[i]<j){
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
//遍历-先遍历物品再遍历背包
for(let j=0;j<=m;j++){
for(let i=1;i<n;i++){
if(weight[i]<j){
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
console.log(dp);
return dp[n-1][m];
}
结果: