01背包理论基础(二维)
一、做题感受&第一想法
没什么好想法,之前没写过背包问题。
二、学习文章后收获
1.暴力解法:回溯
每种物品有“取”与“不取”两种状态。可以当作一个组合问题。
时间复杂度就是
o
(
2
n
)
o(2^n)
o(2n),这里的n表示物品数量。
所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!
2.动态规划
- 动规五要素分析
- dp[i][j]的含义: 背包容量为 j 时,从0~ i 号物品中任取,能得到的最大价值
- 递推公式:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
; - dp初始化:
- 第一行:当j >= weight[0]时,dp[0][j] = value[0];当j < weight[0]时,dp[0][j] = 0
- 第一列:j = 0,也就是背包容量为0(装不下任何东西),则能装的最大价值也为0,故第一列全为0。
- dp遍历顺序:逐行遍历
- 推算dp(略)
#include<iostream>
#include<vector>
using namespace std;
int main(){
int m = 0,n = 0;
cin >> m;
cin >> n;
vector<int> weight(m);
vector<int> value(m);
vector<vector<int>> dp(m,vector<int>(n+1,0)); //注意规格!
for(int i = 0;i < m;i++){
cin >> weight[i];
// cout << weight[i] <<endl;
}
for(int i = 0;i < m;i++){
cin >> value[i];
}
//初始化dp
for(int i = 0;i <= n;i++){
if(i < weight[0]) dp[0][i] = 0;
else if(i >= weight[0]) dp[0][i] = value[0];
}
//遍历并计算dp
for(int i = 1;i < m;i++){
for(int j = 1;j <= n;j++){
if(j - weight[i] >= 0) dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
else dp[i][j] = dp[i-1][j];
}
}
cout << dp[m-1][n] << endl;
return 0;
}
3.注意点
- dp数组的规模:如果有m个物品,背包容量为n,则dp数组规模为m*(n+1),申请代码为
vector<vector<int>> dp(m,vector<int>(n+1,0))
(因为物品从0开始标号到m-1,背包容量从0到n) - 递推公式的说明:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
- 不装入物品 i :
dp[i-1][j]
- 装入物品 i :
dp[i-1][j-weight[i]]+value[i]
- 不装入物品 i :
- 用递推公式时注意判断
dp[i-1][j-weight[i]]
是否越界(j-weight[i] > 0
才行)
01背包理论基础(滚动数组,一维)
一、做题感受&第一想法
无。
二、学习文章后收获
1.滚动数组注意点
- 递推公式:
dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
- 遍历顺序:
- for循环控制物品 i ,从前往后,物品序号1~(m-1)
- for循环控制背包容量 j ,从后往前,容量从n到1(或0)
- 说明:为什么背包容量要从后往前?答:因为如果从前往后,则修改dp过程中相当于把上一层的信息覆盖了,后续dp更新要用到上一层的信息,但是前面的更新很可能已经把原数据覆盖!
#include<iostream>
#include<vector>
using namespace std;
int main(){
int m,n;
cin >> m;
cin >> n;
vector<int> dp(n+1,0);
vector<int> space(m);
vector<int> value(m);
for(int i = 0;i < m;i++){
cin >> space[i];
}
for(int i = 0;i < m;i++){
cin >> value[i];
}
//初始化
for(int i = 0;i <= n;i++){
if(i >= space[0]) dp[i] = value[0];
else if(i < space[0]) dp[i] = 0;
}
//生成dp
for(int i = 1;i < m;i++){ //遍历物品:从前往后
for(int j = n;j >= 1;j--){ //遍历背包:从后往前,一直到第二个元素
if(j-space[i] >= 0)
dp[j] = max(dp[j],dp[j-space[i]] + value[i]);
}
}
cout << dp[n] << endl;
return 0;
}
416. 分割等和子集
一、做题感受&第一想法
1.第一想法:回溯
组合问题+剪枝
但是超出时间限制
代码:(仅记录!!超时了)
class Solution {
public:
bool canPartition(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<bool> used(nums.size(),false);
int sum = 0;
vector<int> path;
for(int i = 0;i < nums.size();i++){
sum += nums[i];
}
if(sum % 2 == 1) return false;
sum /= 2;
bool flag = false;
backtracking(sum,nums,path,0,0,flag,used);
return flag;
}
void backtracking(int sum,vector<int> nums,vector<int>& path,int curSum, int startIndex,bool &flag,vector<bool> &used){
if(sum == curSum){
flag = true;
return;
}
else if(curSum > sum) return;
else if(startIndex == nums.size()) return;
for(int i = startIndex;i < nums.size();i++){
if(i >= 1 && nums[i] == nums[i-1] && used[i-1] == false) continue; //used[i-1]而不是used[i]!!!
path.push_back(nums[i]);
curSum += nums[i];
used[i] = true;
backtracking(sum,nums,path,curSum,i + 1,flag,used);
curSum -= nums[i];
path.pop_back();
used[i] = false;
}
return;
}
};
二、学习文章后收获
1.动态规划01背包问题
- 对比经典01背包问题:本题中的nums数组,既是背包问题中的价值数组value,也是重量数组weight!
- 先求出nums所有元素之和sum,如果为奇数必然不可划分;如果为偶数则取
n = sum / 2
作为背包最大容量。 - 本问题可化为01背包问题:求容量为n的背包可承载的最大重量是多少?即求dp[n]
- dp[j]:容量为j的背包最多可以装多少重量的东西(物品序号0~i),天然地有
dp[j] <= j
- 如果
dp[n] == n
,说明找到了一种划分使两子集之和相等,返回true。如果dp[n] < n
,则返回false。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(int i = 0;i < nums.size();i++){
sum += nums[i];
}
if(sum % 2 == 1) return false;
int n = sum/2;
vector<int> dp(n+1,0); //dp[j]容量为j的背包最多可以装多少重量的东西(物品序号0~i)
//初始化
for(int i = 0;i <= n;i++){
if(nums[0] <= i) dp[i] = nums[0];
else dp[i] = 0;
}
//生成dp
for(int i = 1;i < nums.size();i++){
for(int j = n;j >= 1;j--){
if(j >= nums[i]) dp[j] = max(dp[j],dp[j-nums[i]] + nums[i]);
}
}
if(dp[n] == n) return true;
else return false;
}
};
2.本题可抽象为:01背包最大重量问题
经典的01背包问题,是求每个容量下的最大“价值”。
而本题可看作,求每个容量下的最大承载“重量”。
天然地,dp[j] <= j
。
且本题只需要一个nums数组,既是背包问题中的价值数组value,也是重量数组weight。