数据结构算法笔记|10 动态规划
数据结构算法笔记|10 动态规划
10.1 动态规划解题步骤
- 确定dp数组以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
10.2 0-1背包
10.2.1 价值最大问题
问题
有n件物品和最多能背重量为W的背包,第i件物品重量是weight[i],得到的价值是value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
暴力解法
用回溯遍历取与不取,时间复杂度o(2^n)。
动态规划思路1:二维数组法
确定dp数组以及下标的含义
二维数组dp[i][j]
含义:[0,i]之间的物品任取,放到容量为j的背包中,所能得到的最大价值。
确定递推公式
不放物品i:dp[i-1][j]
放物品i:dp[i-1][j-weight[i]]+value[i]
递推公式:dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
dp数组如何初始化
不放物品i:由表格上方元素推出
放物品i:由表格左上方元素推出
初始化:
最上一行,从物品0的重量开始,初始化为物品0的价值
最左一列,背包容量为0,价值为0
重量0 重量1 重量2 重量3 物品0 0 15 15 15 物品1 0 物品2 0 确定遍历顺序
通常先遍历物品,再遍历背包(只要上方和左上方有值即可,因此可以颠倒)
举例推导dp数组
动态规划思路2:一维数组法
总体思路:把上一层拷贝到当前层,直接使用一层数组更新
确定dp数组以及下标的含义
一维数组dp[j]:容量为j的背包所能容纳物品的最大价值
确定递推公式
dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
dp数组如何初始化
dp[0]=0
确定遍历顺序
先遍历物品,再倒序遍历背包
倒序的必要性:一维数组法相当于二维数组的当前层重复利用了上一层的空间,正序遍历会先把后面要用到的dp[j-weight[i]]更新,导致后续数组更新时会取不到想要的,原先二维数组对应的左上角的旧值
1
2
3
4for(i = 0; i < 物品数量; i++){
for(j = bagweight; j >= weight[i];j--){
}
}举例推导dp数组
dp[2]=dp[2-1]+15=15
dp[1]=dp[1-1]+15=15
例题
以卡码网46题为例
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。
小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。
输入
第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。
第二行包含 M 个正整数,代表每种研究材料的所占空间。
第三行包含 M 个正整数,代表每种研究材料的价值。
输出
输出一个整数,代表小明能够携带的研究材料的最大价值。
题解
1 |
|
注意:重量,价值和空间均从0编号,但根据物理含义,重量和价值的最大下标是M-1,空间的最大下标是N.
10.2.2 背包装满问题
问题
用nums装满容量为x的背包,有几种方法
动态规划思路1:二维数组法
确定dp数组以及下标的含义
二维数组dp[i][j]
含义:[0,i]之间的物品任取,放到容量为j的背包中,能装满的方法。
确定递推公式
不放物品i:dp[i-1][j]
放物品i:dp[i-1][j-weight[i]]
递推公式:放i和不放i的方法数之和 dp[i][j] = dp[i-1][j]+dp[i-1][j-weight[i]]
dp数组如何初始化
数组第一行,恰好能放下物品0时方法为1:dp[0][nums[0]]=1
数组第一列, 装满容量为0的空间的方法:dp[i][0]=2^t, t表示重量为0的物品的数目
确定遍历顺序
通常先遍历物品,再遍历背包(只要上方和左上方有值即可,因此可以颠倒)
举例推导dp数组
动态规划思路2:一维数组法
递推公式:dp[j] += dp[j-weight[i]]
例题
题目
给你一个非负整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-‘ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-‘ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
思路
设加法总和为x,则有x-(sum-x)=target,即x=(sum+target)/2
问题转化成背包装满容量为(sum+target)/2的背包,根据定义有sum < |target|,且(sum+target)/2为整数。
代码
1 | class Solution { |
注意:明确定义背包容量和物品属性,并在遍历之前排除不符合条件的情况。
10.2 完全背包
动态规划:二维数组法
递推公式
不放物品i: dp[i-1][j]
放物品i: 背包物品无限,因此是在物品i和容量j-weight的价值基础上增加价值 dp[i][j-weight[i]]+value[i]
递推公式: dp[i][j]=max(dp[i-1][j], dp[i][j-weight[i]]+value[i])
初始化
背包物品无限,因此物品0可以根据容量无限添加
遍历顺序
求组合数外层遍历物品,求排列数外层遍历背包
例题
以卡码网52题为例
问题
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的重量,并且具有不同的价值。
小明的行李箱所能承担的总重量是有限的,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料可以选择无数次,并且可以重复选择。
输入
第一行包含两个整数,n,v,分别表示研究材料的种类和行李所能承担的总重量
接下来包含 n 行,每行两个整数 wi 和 vi,代表第 i 种研究材料的重量和价值
输出
输出一个整数,表示最大价值。
代码1:二维数组法
1 |
|
注意:由于i=0已经初始化,且递推公式定义要求i>1,因此遍历时从i=1开始
代码2:一维数组法
1 |
|