动态规划---背包问题

动态规划常用框架

定义dp数组,确定其含义;
初始化dp数组;
求解递推公式

一、0-1背包

问题描述

有n种物品, 每种物品只有一个,放入容量为w的背包中,求可以放入的最大价值

二维数组解题

原理

  • dp[i][j]数组表示在0到i件商品中任取商品放入容量为j的背包中最大价值
  • 需要初始化二维数组dp第一行和第一列
  • 递推公式的确定(由两部分组成)
    不放物品i:背包中的价值和dp[i - 1][j]
    放物品i: dp[i - 1][j - weight[i]] + value[i] (物品i的价值)
    故, dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
  • 遍历顺序可以调换(可以先遍历物品,再遍历容量;反过来也可以),找个例子手推一遍就会发现,遍历顺序的调换不影响递推公式的确定(遍历顺序的调换其实改变的是二维数组的填充顺序)

JS代码

/**
 * 二维数组
 * @param {Array} weight 物品重量数组
 * @param {Array} value  物品价值数组
 * @param {Number} size  背包容量
 */
function testWeightBagProblem(weight, value, size) {
  // 物品的个数
  let len = weight.length
  let dp = Array(len)
    .fill()
    .map((item) => Array(size + 1).fill(0))

  // 初始化第一行
  for (let j = weight[0]; j < size; j++) {
    dp[0][j] = value[0]
  }
  for (let i = 1; i < len; i++) {
    for (let j = 1; j <= size; j++) {
      if (j < weight[i]) {
        dp[i][j] = dp[i - 1][j]
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
      }
    }
  }
  return dp[len - 1][size]
}

console.log(testWeightBagProblem([1, 3, 4], [15, 20, 30], 4))

一维数组解题

原理

  • 就是将二维数组进行压缩,外层遍历物品,内层遍历容量(必须使用倒序),这个遍历顺序是不能变的
  • 内层遍历容量使用倒序的原因
    二维数组解题时,计算dp[i][j]用的是dp[i-1]那一行的元素;而一维数组解题,计算dp[j]用的是本行元素,如果正序遍历,前面的数据改了,后面的数据再计算时就会出错(每个物品可能会放入背包多次),但倒序遍历就会避免这个问题
  • 遍历每一个物品,一维数组都会重用,然后在此基础上修改一维数组;找个例子手推一遍就会发现把每个物品的一维数组合并就是之前的二维数组
  • dp[j]表示容量为j的背包中可以放入的最大价值
  • 递推公式
    dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
  • 将dp[j]初始化为0,这样不会影响通过递推公式求解dp[j]

JS代码

/**
 * 一维数组
 * @param {Array} weight 物品重量数组
 * @param {Array} value  物品价值数组
 * @param {Number} size  背包容量
 */
function testWeightBagProblem2(weight, value, size) {
  // 初始化dp数组
  let dp = Array(size + 1).fill(0)
  // 外层遍历物品,内层遍历容量(倒序,保证每一个物品只会放入一次),遍历顺序不可变
  for (let i = 0; i < value.length; i++) {
    for (let j = size; j >= weight[i]; j--) {
      dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
    }
  }
  return dp[size]
}

二、完全背包

问题描述

有n种物品, 每种物品有无限个,放入容量为w的背包中,求可以放入的最大价值

一维数组解题

原理

  • 和0-1背包的一维数组解题一样,只不过在遍历容量的时候是正序遍历
  • 遍历物品和遍历容量的顺序可以调换

JS代码

function testWeightBagProblem(weight, value, size) {
  let dp = Array(size + 1).fill(0)
  for (let i = 0; i < value.length; i++) {
    for (let j = weight[i]; j <= size; j++) {
      dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
    }
  }
  return dp[size]
}

console.log(testWeightBagProblem([1, 3, 4], [15, 20, 30], 4))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值