动态规划算法题梳理

1,01背包和完全背包概念理解

在这里插入图片描述

2,01背包问题详解

(1)已知信息分析

已知条件:

  • n件物品

  • 每件物品的重量数组

  • 每件物品的价值数组

  • 物品只能使用一次
    求解答案:

  • 哪些物品放入背包使得背包中物品的价值最大

(2)例子题目

背包的最大重量为4

weightvalue
物品0115
物品1320
物品2430

求背包可以放置最多价值的物品。

(3)解法

解法一:暴力解法

每件物品有两种状态,选或者不选,那么就可以使用回溯法搜索所有的情况。时间复杂度为o(2^n)

解法二:使用动态规划进行01背包问题

动态规划五部曲

  • dp数组定义以及下标的含义

  • 确定递推公式

  • 确定dp数组的初始值

  • 确定遍历顺序

  • 举例推导dp数组
    首先根据例子题目进行推导

  • 定义dp数组:dp[i][j]的含义就是从下标0-i中的物品任取一个,放进容量为j的背包的最大价值总量的值。

背包容量 0背包容量 1背包容量 2背包容量 3背包容量 4
物品0
物品1
物品2
  • 确定递推公式
    有两个方向可推出dp[i][j]
  1. 不放物品i的时候 dp[i-1][j]
  2. 放物品i的时候dp[i-1][j - weight[i]] + value[i]
    上面的两个值取最大的一个来作为dp[i][j]的值

dp[i][j]= Math.max( dp[i-1][j],dp[i-1][j - weight[i]] + value[i])

  • 初始化二维数组
    • 从dp定义下手,若是j=0时,那意味着背包的容量为0,那物品无论是多少,那对应的dp[i][0]都是0.
    • 从其他情况下手,在我们的递推公式中,i是由i-1推断出来的,因此必须初始化i=0的情况,当i=0的时候,只有j>=wight[i]的时候,该物品0才能够放进去。
      初始化之后的结果就是:
背包容量 0背包容量 1背包容量 2背包容量 3背包容量 4
物品0015151515
物品100000
物品200000

对于其他的数值,都是会经过计算然后覆盖掉的,所以就可以将其初始化为0。

  • 确定遍历顺序
    根据递推公式我们可以知道,dp[i][j]是由dp[i-1][j]或者dp[i-1][j - weight[i]] 来确定的,因此可以使用从前向后的遍历方式来进行。

关于物品和背包先遍历哪一个,这里是都可以的,但是先遍历物品比较好理解。

先遍历物品的理解是:将物品逐一的放在不同容量的背包里,求出dp数组中每一项的价值最大值。

  • 举例推导数组
    就是将你写好的递推公式,用人工的方式,表示出来,再将程序运行后的结果和你的画出来的表格进行对比,看是否一致,如果不一致就要检查其遍历顺序啦。

  • 代码

// weight是物品重量的数组,value是物品的价值,size是背包的最大容量
var test = fucntion(weight,value,size){
  let len = weight.length
  //声明dp数组
  let dp = new Array(len +1).fill(0).map(v=>new Array(size+1).fill(0))
  // 初始化dp数组
  for(let i = 0;i < len;i++){
    dp[i][0] = 0
  }
  for(let j = 0; j <= size;j++){
    if(j >= weight[0]){
      dp[0][j] = weight[0]
    }
  }
  // 递推公式
  for(let i = 1; i < len;i++){
    for(let j = 1; j <= size;j++){
      if(j > weight[i]){
        dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i]])
      }
    }
  }
  return dp[len][size]
}
解法三:动态规划精简做法。

对于解法二,是使用了二维数组,这儿可以精简为一维数组来进行计算。

  • dp数组的含义以及下标的含义
    dp[j] 就是容量为j的背包所能装的最大价值。

  • 声明递推公式
    dp[j] 有两种选择,一个是dp[j - weight[i]],另一种是就是原始的dp[j],相对于二维数组中的dp[i-1][j]。

dp[j] = Math.max(dp[j],dp[j - weight[i]])

  • dp数组的初始化
    这里需要结合dp数组的定义来进行初始化值,首先dp[0]是容量为0 的背包里所能容纳的最大价值,那肯定就是0了。除了下标是0,其他的下标应该如何初始化呢?首先要看一下递推公式:

dp[j] = Math.max(dp[j],dp[j - weight[i]]),dp[j]要取最大的,那就可以将值初始化为正数的最小值0即可。

  • 确定遍历顺序
    对于一维数组来说,需要从后往前进行遍历,保证物品i只被放进去一次。

并且不可交换背包和物品的遍历顺序。

以上两点可以进行实验

  • 代码
var test = function(weight,value,size){
  let len = weight.length
  //声明dp数组,并且初始化为0
  let dp = new Array(size+1).fill(0)
  //递推公式
  for(let i = 0;i < len;i++){
    for(let j = 0; j <= size;j++){
      dp[j] = Math.max(dp[j],dp[j-weight[i]]+value(i))
    }
  }
  return dp[size]
}

3,完全背包问题详解

(1)已知信息分析

已知条件:

  • n件物品
  • 每件物品的重量数组
  • 每件物品的价值数组
  • 物品可以使用多次(这是和01背包不同的地方)

求解答案:

  • 哪些物品放入背包使得背包中物品的价值最大

(2)例子题目

和01背包中的题目一样。

(3)解法

类比于01背包中的一维数组的解法。

不同的是:

01背包中的核心代码,是先遍历物品,再遍历背包,并且背包是从大到小进行遍历的,是为了保证每个商品只能加一次。

对于完全背包来说,每个商品有无数个,因此需要从小到大进行遍历,并且先遍历物品或者背包都可以。

// 01背包
for(let i = 1;i < nums.length;i++){
    for(let j = count;j >= nums[i];j--){
        dp[j] = Math.max(dp[j],dp[j-nums[i]])
    }
}
//完全背包
for(let i = 1;i < nums.length;i++){
    for(let j = nums[i];j <= count;j++){
        dp[j] = Math.max(dp[j],dp[j-nums[i]])
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值