Day41——动态规划Ⅲ
背包!!
1.理论基础——代码随想录
主要掌握01背包和完全背包
物品数量: 只有一个 —— 01背包;
无数个 —— 完全背包;
不同物品数量不同 —— 多重背包;
按组打包,每组最多选一个 —— 分组背包;
2.纯01背包_kamacoder46
题目描述:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大
思路:
- 暴力方法:所有物品只有两种状态,装或者不装,因此可以用回溯法搜索所有情况,然后选择
- 二维数组dp01背包:
- dp[i][j]表示容量为 j 的背包从前i件(0=<x<i)物品里随便装所取得的物品最大价值
- 当物品价值最大时,第i件物品如果不在背包里,那么dp[i][j] = dp[i-1][j];
如果在背包里,dp[i][j] = dp[i-1][j-w[i]] + v[i];
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]); - 初始化: 背包容量为0,价值为0;dp[i][0] = 0;
从定义可知,i 从 1开始有意义, i > 0;
补充:当 i= 0 时,因为i>0时,dp[i][j]都与dp[i-1][j]相关,因此需要初始化i= 0;如果 j < w[0],那么不用管,本来就放不进去,但如果 j > w[0],此时的dp[0][j]就需要置为v[0]了,因为第一个能放入背包。 - 顺序:i从1 ~ n,j 从 w ~ 0;
先遍历物品和先遍历物品,都差不多 - dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
#include <iostream>
#include <vector>
using namespace std;
int main() {
int M, N;
cin >> M >> N;
vector<vector<int>> item(M, vector<int>(2, 0));
for(int i = 0; i < M; i++) {
cin >> item[i][0];
}
for(int i = 0; i < M; i++) {
cin >> item[i][1];
}
vector<vector<int>> dp(M, vector<int>(N + 1, 0));
for (int j = item[0][0]; j <= N; j++) {
dp[0][j] = item[0][1];
}
for(int i = 1; i < M; i++) {
for(int j = 0; j <= N; j++) {
if(item[i][0] > j) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j], dp[i-1][j-item[i][0]] + item[i][1]);
}
}
cout << dp[M-1][N];
return 0;
}
方法二:一维数组
dp[i][j] = max(dp[i-1][j], dp[i-1][j-item[i][0]] + item[i][1])
可以用dp[i][j]来保存dp[i-1][j],从而转换成一维数组
dp[j] = max(dp[j], dp[j-w[i]+item[i][1]);
#include <iostream>
#include <vector>
using namespace std;
int main() {
int M, N;
cin >> M >> N;
vector<vector<int>> item(M, vector<int>(2, 0));
for(int i = 0; i < M; i++) {
cin >> item[i][0];
}
for(int i = 0; i < M; i++) {
cin >> item[i][1];
}
vector<int> dp(N + 1, 0);
for(int i = 0; i < M; i++) {
for(int j = N; j >= item[i][0]; j--) {
dp[j] = max(dp[j], dp[j-item[i][0]] + item[i][1]);
}
}
cout << dp[N];
return 0;
}
3.leetcode_416分割等和子集
思路:
主要问题在于如何从这个问题联想到动态规划
- 集合的元素相等,则target = sum >> 1; 相当于给定背包容量,和物品重量,如果能分割,那么背包的最大容量就是最大价值,我们只需求出最大价值然后进行比较即可
- dp[j]表示 容量为j的背包最大价值是dp[j]
- dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]);
- 一维数组背包,j 从后向前,否则会重复使用元素
bool canPartition(vector<int>& nums) {
int sum = 0;
for(int i : nums)
sum += i;
if(sum % 2 == 1) return false;
int target = sum >> 1;
vector<int> dp(target + 1, 0);
for(int i = 0; i < nums.size(); i++)
for(int j = target; j >= nums[i]; j--){
dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]);
}
return dp[target] == target;
}