一些算法总结
01背包问题
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大?
二维dp数组01背包
举一个栗子:
背包最大重量为4
物品为:
| 重量 | 价值 | |
|---|---|---|
| 物品0 | 1 | 15 |
| 物品1 | 3 | 20 |
| 物品2 | 4 | 30 |
问背包能背的物品最大价值是多少?
| 重量 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 物品0 | |||||
| 物品1 | |||||
| 物品2 |
确定递推公式
dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
那么可以有两个方向推出来dp[i][j]:
- 不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是
dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以被背包内的价值依然和前面相同。) - 放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
- 所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。
| 重量 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 物品0 | 0 | ||||
| 物品1 | 0 | ||||
| 物品2 | 0 |
在看其他情况。
状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
代码初始化如下:
1 | // 初始化 dp |
那么问题来了,先遍历 物品还是先遍历背包重量呢?
其实都可以!! 但是先遍历物品更好理解。
那么给出先遍历物品
1 | // weight数组的大小 就是物品个数 |
dp[i][j]所需要的数据就是左上角,根本不影响dp[i][j]公式的推导
手动推导顺序:
| 重量 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 物品0 | 0 | 15 | 15 | 15 | 15 |
| 物品1 | 0 | ||||
| 物品2 | 0 |
| 重量 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 物品0 | 0 | 15 | 15 | 15 | 15 |
| 物品1 | 0 | 15 | 15 | 20 | 35 |
| 物品2 | 0 |
| 重量 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 物品0 | 0 | 15 | 15 | 15 | 15 |
| 物品1 | 0 | 15 | 15 | 20 | 35 |
| 物品2 | 0 | 15 | 15 | 20 | 35 |
最终结果就是dp[2][4]。
建议大家此时自己在纸上推导一遍,看看dp数组里每一个数值是不是这样的。
做动态规划的题目,最好的过程就是自己在纸上举一个例子把对应的dp数组的数值推导一下,然后在动手写代码
1 | void DP() { |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 路の小窝!
评论








