数据结构标准入门-背包问题

416 阅读5分钟

不抄袭,不做作,讲自己的理解,如有与您理念不合的地方 啊对对对

背包问题

背包问题主要是指一个给定容量的背包,若干具有一定价值和重量的物品,如何选择物品放入背包让物品的价值变最大。其中又分01背包和完全背包(完全背包指的是:每种物品都有无限件可用)

这里主要研究01背包,但是完全背包也可以转化为完全背包

先模拟一个场景,我是一个小偷,我进了一个商场,然后有4种商品可以给我去偷,电瓶车1号的电池(1000RMB,1kg),电瓶车2号的电池(2000RMB,2kg),电瓶车3号的电池(3000RMB,3kg),电瓶车4号的电池(4000RMB,4kg),但是我只带了一个背包,并且这四号电池每一个型号的电池只有一个。假设我的背包能装1kg,2kg,3kg,4kg这四种情况,我要怎么偷商品,才能够让我今天晚上出门作案的收益最大化?

由于我不知道怎么用掘金画表格,我就吧这个过程说的再详细点:

可以看到,上面这张图画出了我们在不同的背包容量下,我们该怎么选购电瓶才能够让我们出来干的这一趟收益最大化!

好的,以上我们就画出来了这个算法实现的表图,那么我们现在来写算法实现的具体描述

思路分析和图解

  • 算法的主要思想,利用动态规划来解决,每次遍历到第i个物品,更具w[i]和v[i]来确定是否需要将该物品放入背包中,即对于给定的n个物品,设v[i],w[i]分别为第i个物品的价值和重量,C为背包的容量。再令v[i]][j]表示在前i个物品中能够装入容量为j的背包的最大价值。则我们有下面的结果:

  • v[i][0]=v[0][j]=0; //表示填入表的第一行和第一列是0

  • 当w[i]>j时:v[i][j]=v[i-1][j] //当准备加入新增商品的容量大于当前背包容量时,就使用上一个单元格的装入策略

  • 当j>w[i]时,v[i][j]=max{v[i-1][j],v[i]+v[i-1][j-w[i]]}//当准备加入新增商品的容量大于当前背包的容量时,就使用上一个单元格的装入策略

代码实现

  • 定义常量

  •   //每个商品对应的重量let weight=[1,4,3,5,6]// 每个商品对应的价值let value=[1500,3000,2000,5000,6000]let m=4;//背包容量let map=new Array()
    
  • JS创建二维数组

  •   // 创建二维数组开始function initMap(map){  for(let i=0;i<value.length;i++){    map[i]=new Array()    for(let j=0;j<weight.length;j++){      map[i][j]=0    }  }  map=initColumnAndRows(map)  return map}// 创建二维数组结束
    
  • 初始化二维数组的第一行和第一列为0,表示,你如果没有带背包,那么你就拿不了商品

  •   // 初始化第一行和第一列开始function initColumnAndRows(map){  let rowLength=map.length  let columnLength=map[0].length  for(let i=0;i<rowLength;i++){    map[i][0]=0  }  for(let j=0;j<columnLength;j++){    map[0][j]=0  }  map=MethodData(map)  return map}// 初始化第一行和第一列结束
    
  • 背包问题实际解决核心代码

  •   // 动态规划处理函数开始function MethodData(map){  for(let i=1;i<map.length;i++){    for(let j=1;j<map[i].length;j++){      // 如果说当前商品的重量大于你可以支配的重量      if(weight[i-1]>j){ //此处应该要减1        // 那么当前商品的价值就为上面一行的商品价值        map[i][j]=map[i-1][j]      }else{        // 否则的话当前商品的价值就会进行一次比较,看是上一行商品的价值高一点,还是你当前一行上一格商品的价值        // 加上剩余可支配的空间对应能拿到的商品价值高一点        // j-weight[i-1]表示剩余可支配的空间对应能拿到的商品的重量        map[i][j]=Math.max(map[i-1][j],value[i-1]+map[i-1][j-weight[i-1]])      }    }  }  return map;}// 动态规划处理函数结束
    
  • 调用函数代码

  •   map=initMap(map)console.log(map)
    

以上代码解决01背包问题,下面我们来研究完全背包问题

完全背包问题

一个背包,可以放入n种物品,物品j的重量和价值分别为w_j,v_j,j=1,2,\cdots,n,如果背包的最大重量限制是b,怎么样选择放入背包的物品以使得背包的总价值最大?

下面出道例题,方便大家理解:

近期某商场由于周年庆,开启了“0元购”活动。活动中,消费者可以通过组合手中的代金券,实现0元购买指定商品。

聪明的小团想要用算法来帮助他快速计算:对于指定价格的商品,使用代金券凑出其价格即可,但所使用的代金券总面额不可超过商品价格。由于代金券数量有限,使用较少的代金券张数则可以实现价值最大化,即最佳优惠。

假设现有100元的商品,而代金券有50元、30元、20元、5元四种,则最佳优惠是两张50元面额的代金券;而如果现有65元的商品,则最佳优惠是两张30元代金券以及一张5元代金券。

请你帮助小团使用一段代码来实现代金券计算。

该题是美团秋招2020年前端笔试第6题

下面直接贴代码,这题我录了个视频讲解:www.bilibili.com/video/BV1LU…

// 最少的代金券,function fn(coins,price){  // 为了模拟这个数组的下标为0-price,也就是每一块钱,方便我们在消除类似的代金券的时候,进行重复使用  let dp=new Array(price+1).fill(Infinity)  dp[0]=0  // 计算0-100元之间,你每个元素需要几张代金券  for(let i=1;i<=price;i++){    // 在当前价位下最少用几张代金券    for(let coin of coins){      if(i>=coin){        //coin:50        //dp[i]=Infinity dp[i-coin]        dp[i]=Math.min(dp[i],dp[i-coin]+1)      }    }  }  return dp[price]===Infinity?'没有当前价位的商品的代金卷':dp[price]}console.log(fn([50,30,20,5],110))

以上