昨天的两道题目还是有点难的,思路真的很难想到,后面多复习吧,今天开始动态规划中的背包问题。(今天状态不好,最后一题直接看的答案,先玩几天吧,等过年之后继续刷题)
今日任务:
- 01背包问题二维
- 01背包问题一维
- 416.分割等和子集
背包目前只需要弄懂01背包和完全背包弄懂就好了。Leetcode一般都是应用类题目
题目一:01背包问题(二维)
卡玛网题目:【46.携带研究材料】
参考:【代码随想录之01背包问题】
代码实现也是很难想,但是可以理解:
主要的难点有两个:
(1)初始化怎么处理;
(2)递推方程如何推导出来?
按照卡哥的递推五部曲:
(1)确定dp[i][j]数组的定义:表示从0到i的最大包重量的最优值;
(2)递推公式:
(3)初始化:
全部初始化为0,然后上面那一排的当j>weight[0]的时候可以变为value[i];
(4)确定遍历顺序:
从左到右,从上到下即可。
(5)打印bp数组:
#include<iostream>
#include<vector>
using namespace std;
int maxValue(int m, int bagsize){
int weight[m] = {};
int value[m] = {};
for(int i = 0; i < m; ++i) {
cin >> weight[i];
}
for(int j = 0; j < m; ++j) {
cin >> value[j];
}
// m表示有多少种材料,n表示每种材料的价值
vector<vector<int>> dp(m, vector<int>(bagsize+1, 0));
// 进行初始化
for(int j = weight[0]; j<=bagsize; j++){
dp[0][j] = value[0];
}
// 递推方程
for(int i = 1; i<m; i++){
for(int j = 0; j<=bagsize; j++)
if(j < weight[i]) dp[i][j] = dp[i-1][j];
else{
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
}
}
return dp[m-1][bagsize];
}
int main(){
int M, N;
while(cin >> M >> N){
cout << maxValue(M, N) << endl;
}
return 0;
}
题目二:01背包问题(一维)
如何从二维降为一维数组的?
把上一层数据拷贝下来了
按照动规五部曲:
(1)dp[j]:容量为j的背包所能获取的最大价值;
(2)递推公式:
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
(3)初始化:
dp[0] = 0;
(4)遍历顺序:
外层只能是先遍历物品,再遍历不同重量的背包;
(5)打印dp数组
// 一维dp数组实现
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 读取 M 和 N
int M, N;
cin >> M >> N;
vector<int> costs(M);
vector<int> values(M);
for (int i = 0; i < M; i++) {
cin >> costs[i];
}
for (int j = 0; j < M; j++) {
cin >> values[j];
}
// 创建一个动态规划数组dp,初始值为0
vector<int> dp(N + 1, 0);
// 外层循环遍历每个类型的研究材料
for (int i = 0; i < M; ++i) {
// 内层循环从 N 空间逐渐减少到当前研究材料所占空间
for (int j = N; j >= costs[i]; --j) {
// 考虑当前研究材料选择和不选择的情况,选择最大值
dp[j] = max(dp[j], dp[j - costs[i]] + values[i]);
}
}
// 输出dp[N],即在给定 N 行李空间可以携带的研究材料最大价值
cout << dp[N] << endl;
return 0;
}
题目三:分割等和子集
Leetcode题目:【416.分割等和子集】
参考:【代码随想录之分割等和子集】
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
// dp[i]中的i表示背包内总和
// 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
// 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
vector<int> dp(10001, 0);
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
// 也可以使用库函数一步求和
// int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 == 1) return false;
int target = sum / 2;
// 开始 01背包
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]);
}
}
// 集合中的元素正好可以凑成总和target
if (dp[target] == target) return true;
return false;
}
};