🥳前端算法面试--动态规划之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 到本地跑一跑,这样会更清楚

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

相关推荐
腾讯TNTWeb前端团队1 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom4 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪5 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github