我一直都没搞懂01背包问题,直到我开始了面试.....
废话说完,进入正题
题目: 具备 T 容量的背包,要装 N 个商品,每个商品都有对应的重量 w 和价值 v ,如何保证背包在最大容量下装入的商品具备最大价值 ? 在开始计算前,先列出一个假设图表,方便理解:
Q: 具备 T 容量的背包,要装 N 个商品,每个商品都有对应的重量 w 和价值 v ,如何背包在最大容量下装入的商品具备最大价值 ?
定义一个假设列表,寻找列表的规律:
tip: 第一列假设容量T=0 和 第一行假设物品信息都是0的情况没有太大意义,因为什么都放不了肯定这一行最大价值都是0,但目的为了方便第几个后面列表索引是从0开始传入入参。这个初始化表格可看成一个二维数组👇 dp,初始化后我们开始逐个填写最大价值替换默认0
const dp =[ [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0] ]
①当假设物品数量 N=1, 物品重量w1 = 2,物品价值v1 = 1 时:dp[N][T] 如下
假设 容量T=0时,不够放w1,dp[1][0] = 0;
假设 容量T=1时,不够放w1,dp[1][1] = 0;
假设 容量T=2时,够放w1=2,dp[1][2] = v1,所以 dp[1][2] = 1;
T=3 、 T=4 以此类推 都够放w1, dp[N][T] = 1。
N=1时,只有一个物品,所以只要够放背包就直接设置其价值,
所以第一行时**【公式1】**为:
if(w1>T){
dp[N][T] = 0
}else { //
dp[N][T] = v1
}
②当假设物品数量 N=2, 加入了物品重量w2 = 1,物品价值v2 = 3时:dp[N][T] 如下:
假设 容量T=0时,不够放w1、w2,dp[2][0] = 0;
假设 容量T=1时,够放w2=1, 及 w2 <= T,但是需要比较w1 \ w2价值,比较方式可采用与上一层及为只有1个商品时的已计算过的情况进行比较,
其他行的【公式2】为:
Math.max(
上一层N-1时同容量T最大价值dp[N-1][T] ,
(当前层物品价值v2) + (上一层容量T=2 - 这一层用了的重量w2的情况下的最大价值) ,为 v2 + dp[N -1][T - w2]
)
同理:假设 容量T=2时,够放w2 或者 w1,及w2 < T,但是需要比较w1 \ w2价值,比较方式可采用与上一层及为只有1个商品时的已计算过的情况进行比较,伪代码公式为:
Math.max(
上一层N-1时同容量T最大价值dp[2-1][2] ,
(当前层物品价值v2) + (上一层容量T=2 - 这一层用了的重量w2的情况下的最大价值) ,为 v2 + dp[2-1][2 - w2]
)
T=3 、T=4 计算方式与上面一致。
③当假设物品数量 N=3, 加入了物品重量w3 = 3,物品价值v3 = 2时:dp[N][T] 如下:
假设 容量T=0时,不够放w1、w2、w3,dp[3][0] = 0;
假设 容量T=1时,不够放w3,及w3>T,但是我们上一层N-1已经完成了比较,直接取上一层 N-1 同T情况比较过的值来用就行,
所以当当前行物品w3 > 背包容量T,
则【公式3】为:dp[N][T] = dp[N-1][T] 到此,已经可以根据相同的规则来抒写计算公式来填充这个表格。
可以看出,只要我们通过循环行N,再循环每个T进行判断负值,这个表格的价值填充完毕后最大价值dp[N][T] 就是表格最后一个值,就计算出来了,下面是实现方法:
window.onload = function(){
// 背包总共可承重量
const T = 7;
// 商品数量
const N = 4;
// 商品重量 集合 ,wList[1] 对应第1个物品的重量
const wList = [2, 1, 3,4]
// 商品价值 集合, vList[1] 对应第1个物品的价值
const vList = [1,3, 2,4]
console.log(getMaxValue(T,N,wList,vList))
function getMaxValue(T,N,wList,vList){
// 定义出我们的dp价值二维数组
let dpInit = new Array(N+1).fill(0).map(()=>Array(T+1).fill(0))
let dp = JSON.parse(JSON.stringify(dpInit))
console.log('初始化dp=',dpInit)
for (let x = 1; x <= N; x++) { // 循环商品可放顺序
for (let y = 1; y <= T; y++) { // 循环假设的背包总可承重
if(x===1){ // 第一行时 取值不想上取
if(wList[x-1] > y) { // 当前物品重量 > 可承载量 , 啥都放不了
dp[x][y] = 0;
}else { // 背包够放这个物品,总价值dp[1][y] = 这个物品的价值 vList[x]
dp[x][y]= vList[x-1]
}
}else { // 第二行开始,
if(wList[x-1] > y) { // 当前物品重量 > 可承载量 , 啥都放不了
dp[x][y] = dp[x-1][y];
}else { // 背包够放这个物品,,总价值dp[x][y] = 上一层x,具备y的容量
// 或者 当前这一层物品x的价值 vList[x] + [上一层开始计算物品] [具备的容量y - 当前用了的重量wList[x]]]
dp[x][y] = Math.max(dp[x-1][y],vList[x-1] + dp[x-1][y-wList[x-1]])
}
}
}
}
console.log('结果dp=',dp)
console.log('具备'+N+'个物品时'+', '+'背包承重='+T+' 下的最大价值👇')
console.log(dp[N][T])
// 返回最大情况的 物品数量N 和 最大背包承重 的价值,就是最大价值。
return dp[N][T];
}
}
今天,你学废了吗?