完全背包
- 二维dp数组完全背包
- 1. 确定dp数组以及下标的含义
- 2. 确定递推公式
- 3. dp数组如何初始化
- 4. 确定遍历顺序
- 5. 举例推导dp数组
- 完整代码:
- 一维滚动dp数组完全背包
- 1.确定dp数组以及下标的含义
- 2. 确定递推公式
- 3. dp数组如何初始化
- 4. 确定遍历顺序
- 5. 举例推导dp数组
- 完整代码:
完全背包 :有一个背包的容积为V,有N个物品,每个物品的体积为v[i],权重为w[i],每个物品可以取无限次放入背包中,背包所有物品权重和最大是多少?
背包最大重量为4,每个物品有无限个,物品的重量和价值如下:
重量 | 价值 | |
---|---|---|
物品0 | 1 | 15 |
物品1 | 3 | 20 |
物品2 | 4 | 30 |
二维dp数组完全背包
1. 确定dp数组以及下标的含义
dp[i][j]
数组表示从前i个物品中任意取,放进容量为j的背包中所得到的物品的最大价值。
2. 确定递推公式
有两个方向推出来dp[i][j]
:
-
不放物品i:由
dp[i - 1][j]
推出,即背包容量为j
,里面不放物品i
的最大价值,此时dp[i][j]
就是dp[i - 1][j]
。(其实就是当物品
i
的重量大于背包j
的重量时,物品i
无法放进背包中,所以背包内的价值依然和前面相同。) -
放物品i:由
dp[i][j - weight[i]]
推出,注意这里和01背包的区别,因为可以重复放物品i,所以是dp[i][j - weight[i]]
,表示背包容量为j - weight[i]
的时候任取物品i的最大价值 -
递归公式:
dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);
- 如果物品i的重量大于背包容量j时,背包放不下,就不能选择物品i,
dp[i][j] = dp[i - 1][j]
; - 如果物品i的重量小于等于背包容量j时,
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
- 如果物品i的重量大于背包容量j时,背包放不下,就不能选择物品i,
3. dp数组如何初始化
关于初始化,一定要和dp数组的定义一致。
两种初始化情况:
- 背包容量j为0,不管选择哪些物品,所得到的物品最大价值一定为0,所以
dp[i][0] = 0
; - 物品数量为0时,不论背包容量多大,所得到的物品最大价值一定为0,所以
dp[0][j] = 0
。
dp[i][j] (物品\背包) | 0 | 1 | 2 | 3 | 4 |
无物品 | 0 | 0 | 0 | 0 | 0 |
物品0 | 0 | ||||
物品1 | 0 | ||||
物品2 | 0 |
4. 确定遍历顺序
有两个遍历的维度:物品和背包,那么问题来了,先遍历物品还是先遍历背包重量呢?
其实都可以,因为递推公式为dp[i][j] = dp[i - 1][j]
和dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
这两种情况,不论哪种,dp[i - 1][j]
和dp[i][j - weight[i]]
都在dp[i][j]
的左上角方向,而两种遍历顺序都能够获取到dp[i][j]
左上角方向的值,先遍历物品会更好理解
5. 举例推导dp数组
dp[i][j] (物品\背包) | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
无物品 | 0 | 0 | 0 | 0 | 0 |
物品0(重量1,价值15) | 0 | 15 | 30 | 45 | 60 |
物品1(重量3,价值20) | 0 | 15 | 30 | 45 | 60 |
物品2(重量4,价值30) | 0 | 15 | 30 | 45 | 60 |
完整代码:
/*********************************************************************
完全背包
有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
每件物品有无限个,即一个物品可以放入背包多次,求解将哪些物品装入背包里物品价值总和最大。
重量 价值
物品0 1 15
物品1 3 20
物品2 4 30
*********************************************************************/
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;// 先遍历物品,在遍历背包
int bag1(vector<int> weight, vector<int> value, int bagweight) {vector<vector<int>> dp(weight.size() + 1, vector<int>(bagweight + 1, 0));for (int i = 1; i <= weight.size(); i++){for (int j = 0; j <= bagweight; j++){ // 遍历背包if (j < weight[i - 1]){dp[i][j] = dp[i - 1][j];}else{// 注意这里跟01背包只有下面一个下标不同,那就是“放i”这个选择,因为是可以重复放的,所以是dp[i]dp[i][j] = max(dp[i - 1][j - 1], dp[i][j - weight[i - 1]] + value[i - 1]);}}}for (auto x : dp) {for (auto xx : x) {cout << xx << ' ';}cout << endl;}return dp[weight.size()][bagweight];
}int main(){vector<int> weight = { 1, 2, 3, 4 };vector<int> value = { 10, 21, 30, 43 };int bagweight = 5;int ret = bag1(weight, value, bagweight);cout << ret << endl;system("PAUSE");return 0;
}
一维滚动dp数组完全背包
1.确定dp数组以及下标的含义
dp[j]
表示容量为j的背包中所得到的物品的最大价值。
2. 确定递推公式
递推公式变为dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
。
3. dp数组如何初始化
dp[0] = 0即可
4. 确定遍历顺序
01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次;而完全背包的物品是可以添加多次的,所以要从小到大去遍历。
5. 举例推导dp数组
完整代码:
/*********************************************************************
完全背包
有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
每件物品有无限个,即一个物品可以放入背包多次,求解将哪些物品装入背包里物品价值总和最大。
重量 价值
物品0 1 15
物品1 3 20
物品2 4 30
*********************************************************************/
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;// 先遍历物品,在遍历背包
int bag1(vector<int> weight, vector<int> value, int bagweight) {vector<int> dp(bagweight + 1, 0);// 先遍历物品,再遍历背包for (int i = 0; i < weight.size(); i++) { // 遍历物品for (int j = weight[i]; j <= bagweight; j++) { // 遍历背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}}return dp[bagweight];
}int main(){vector<int> weight = { 1, 2, 3, 4 };vector<int> value = { 10, 21, 30, 43 };int bagweight = 5;int ret = bag1(weight, value, bagweight);cout << ret << endl;system("PAUSE");return 0;
}