🥳前端算法面试--动态规划之0-1背包问题-每日一练

今天分享的内容是动态规划的经典问题--0-1 背包问题

0-1背包问题的描述如下:给定一组物品,每种物品都有自己的重量和价值,背包的总容量是固定的。我们需要从这些物品中挑选一部分,使得背包内物品的总价值最大,同时不超过背包的总容量。

举个例子:假设这组物品的质量分别是:

javascript 复制代码
const weight = [2, 2, 6, 4, 3];

背包总容量为 9,请问将这些物品装入背包,最多能装多少,分别是哪些?

思路分析

之前用回溯算法解决过这个问题:但回溯算法有其自身的局限性--指数级别的复杂度。那这篇文章就和上篇文章一样,用动态规划的思路把 0-1 背包问题重新解决一遍

回溯算法的思路是,每种可能都去尝试,即遍历所有的可能,找到最优解。而动态规划不会傻傻地遍历所有的可能性,而是会去掉重复的路径,在剩下的路径下继续探索

背包的总容量是 9,那么不看具体的物品重量,放入背包的重量的可能性一共有 9 种,动态规划将会使计算范围框定在这 9 种范围之内,并且相同重量的物品组合,只保留其中一种。这样下来,动态规划的复杂度就是 n*m 了,n 是物品的种类,m 是背包的总容量

看不明白?没关系,看看下面的过程分析就懂啦

过程分析

weight = [2, 2, 6, 4, 3];

代码的实现就是填满上面的表格。行表示,该行的物品不放的重量种类;列表示背包中物品重量之和

上面第一个行填了两个 true,第一个 true 表示不放 1 号物品时,背包的总重量是 0kg;第二个 true 表示放 1 号物品时,背包的总重量是 2kg

继续填第二行

weight = [2, 2, 6, 4, 3];

共有 3 个 true, 第一个 true 和 第二个 true 表示不放入 2 号物品时,背包的重量可能性--有 0kg2kg 两种可能性;第三个 true 表示,同时放 1 号物品和 2 号物品的情况。

其实,大家仔细想想,如果只放 2 号物品,不放 1 号物品时,背包的重量是多少?也是 2kg,所以第二个 true 也表示只放 2 号物品,不放 1 号物品的情况。这里动态规划将两个 2kg 的情况合并成一种情况。

以至于 1 号和 2 号物品的放与不放有 2^2 种情况,但是组合成的重量却只有 3 种情况. 这就是动态规划提升性能的关键

继续往下看

weight = [2, 2, 6, 4, 3];

第三行共有 5 个 true,前 3 个 true 直接看成是不放 3 号物品时,背包重量的可能性。后面两个 true,一个是 6kg,是只放 3 号物品的情况;另一个是 8kg,可以看成是放 1 号和 3 号物品的情况,也可以看成是 2 号和 3 号物品的情况。

3 种物品共有 2^3 情况,但是动态规划将其合并成 5 种情况。

true 的填写是有规律的,首先填写不放该行物品的情况,即直接照抄上一行的内容,然后从前往后依次对每个 true 的重量加上自身重量,比如第 3 行的第 4 个 true,就是第一个 true 的 0kg 加上 6kg 得到的。第 3 行的第 5 个 true ,是第二个 true 的 2kg 加上 6kg 得到的。再往后相加的话就超过 9kg 了,就停止相加。

大家可以试着推推第 4 行的内容


第 4 行:

weight = [2, 2, 6, 4, 3];

第 5 行:

weight = [2, 2, 6, 4, 3];

可以看到第 5 行的最后一格是 true,也就说明,物品的组合中,是可以将背包填满的。所以背包可以装入的最大重量是 9kg,问题解决!

代码实现

javascript 复制代码
const weight = [1, 2, 2, 6, 5, 8];

const findWeight = (weight) => {
  const store = Array(weight.length)
    .fill(1)
    .map(() => Array(packageWeight + 1).fill(false));
  
  store[0][0] = true;
  
  store[0][weight[0]] = true;

  for (let i = 1; i < store.length; i++) {
    // 不放第i个物品
    for (let j = 0; j <= packageWeight; j++) {
      if (store[i - 1][j]) store[i][j] = true;
    }

    // 放第i个物品
    for (let j = 0; j <= packageWeight - weight[i]; j++) {
      if (store[i - 1][j]) store[i][j + weight[i]] = true;
    }
  }

  for (let i = packageWeight; i >= 0; i--) {
    if (store[weight.length - 1][i]) {
      console.log("the max weight is ", i);
      break;
    }
  }
}
  1. 定义一个数组weight,表示物品的重量。变量packageWeight,表示背包的容量。接着实现了一个名为findWeight的函数,该函数接受一个参数weight,即物品重量的集合

在findWeight函数中,首先创建一个二维数组store,用来表示上面过程分析的表格,表格的内容初始化为 false。store 的行和列含义也和上面表格一致。在遍历 store 之前,先初始化第一行的内容,即放与不放 0 号物品的两种情况。

遍历 store 数组,从第二行开始,也就是 i==1。每一行先考虑不放第 i 个物品。然后再计算放入第 i 个物品的时候,这一行的内容有什么变化。

遍历完所有的行,store 数组中最后一行的最后一个 true 所表示的重量,就是我们要的答案啦

执行代码

javascript 复制代码
findWeight(weight);
// the max weight is  9

这是 store 的内容:

换个数据试试:

javascript 复制代码
const weight = [2, 2, 6, 4, 4];
findWeight(weight);
// the max weight is  8

这是 store 中的内容情况:

总结

这篇文章讲了如何用动态规划来解决 0-1 背包问题,其中有解题的思路分析,还有具体的过程分析,每个过程都有图片,很清晰了。相信大家对着图片多看几遍,就知道动态规划是怎么解决问题的了。最后还有 JS 代码实现,大家可以 copy 到本地跑一跑,这样会更清楚

可以评论区留言哦。我每天都会分享一篇算法小练习,喜欢就点赞+关注吧

相关推荐
GDAL12 分钟前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js
六卿13 分钟前
react防止页面崩溃
前端·react.js·前端框架
许野平28 分钟前
Rust: enum 和 i32 的区别和互换
python·算法·rust·enum·i32
chenziang134 分钟前
leetcode hot100 合并区间
算法
chenziang135 分钟前
leetcode hot100 对称二叉树
算法·leetcode·职场和发展
z千鑫39 分钟前
【前端】详解前端三大主流框架:React、Vue与Angular的比较与选择
前端·vue.js·react.js
szuzhan.gy1 小时前
DS查找—二叉树平衡因子
数据结构·c++·算法
m0_748256141 小时前
前端 MYTED单篇TED词汇学习功能优化
前端·学习
一只码代码的章鱼2 小时前
排序算法 (插入,选择,冒泡,希尔,快速,归并,堆排序)
数据结构·算法·排序算法
青い月の魔女2 小时前
数据结构初阶---二叉树
c语言·数据结构·笔记·学习·算法