背包问题
0-1背包问题
1. 0-1背包问题
题目描述:
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
题目分析:
设f(n,w)前n件物品放到一个容量为w的背包中可以获得最大价值,
考虑我们的子问题,将前i件物品放到容量为v的背包中,若我们只考虑第i件物品时,它有两种选择,放或者不放。
如果第n件物品不放入背包中,那么问题就转换为:将前in- 1件物品放到容量为v的背包中,带来的收益f(n-1)
如果第n件物品能放入背包中,那么问题就转换为:将前n - 1件物品放到容量为w - weight[i]的背包中,带来的收益f(n-1) + cost[n]则有
代码
int bag_problem(const vector<int>&weight,const vector<int>&value,const int bagweight) {
int obj_size = weight.size();//物品数量
vector<vector<int>> dp(obj_size, vector<int>(bagweight+1, 0));//背包容量从0到bagweight
for (int k = weight[0]; k <= bagweight; k++) {
dp[0][k] = value[0];
}
for (int i = 1; i < obj_size; i++) {
for (int j = 1; j <= bagweight; j++) {
if (weight[i] > j) 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[obj_size-1][bagweight];
}
代码简化
上面的代码中,dp(i)的求解只与dp(i-1)有关,与dp(i-2),dp(i-3)....都无关,因此前面的dp(i-2),...都可以覆盖掉
因此可以只用一个一维的dp数组表示,
dp[j]表示从前i件物品选取任意物品装入容量为j的背包所得的最大价值;注意:为了便于理解前面还是加上了i,实际上dp[j]相当于前面二维dp的dp(i:0---n,j)
递推公式
i从0到i
因为求dp[j]依赖于dp[0到j]的值,因此,在i的一次循环内,应该先求j比较大的dp[j],再依次求j比较小的dp[j],这样才能保证求dp[j]的时候使用的是上一轮循环求得dp[0-j]的值,而不是在此轮循环被更新的dp[0-j]
int bag_problem(const vector<int>&weight, const vector<int>&value, const int bagweight) {
vector<int> dp(bagweight + 1, 0);//dp[j]表示在容量为j时能获得的最大价值
for (int i = 0; i < weight.size(); i++) {//依次求在0-i件物品随机选取放入容量为0-bagweight的背包的最大价值
for (int j = bagweight; j >= weight[i]; j--)
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
return dp[bagweight];
}
2.分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
分析
将一个数组划分为两个元素和相等的子集,首先,整个数组和得是偶数
设整个数组和为X,在整个数组里找某些元素使这些元素的和为X/2,找到了则返回true,反之返回false
可以类似与背包问题:
背包的体积为X / 2
背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
背包如果正好装满(达到最大价值),说明找到了总和为 sum / 2 的子集。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for (int ele : nums) sum += ele;
if (sum % 2 != 0) return false;
int bag_weight = sum / 2;
vector<vector<int>> dp(nums.size(), vector<int>(bag_weight + 1, 0));
for (int k = nums[0]; k <= bag_weight; k++) dp[0][k] = nums[0];
for (int i = 1; i < nums.size(); i++) {
for (int j = 1; j <= bag_weight; j++) {
if (j < nums[i]) dp[i][j] = dp[i - 1][j];
else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
}
}
}
return dp[nums.size() - 1][bag_weight] == bag_weight;
}
};
一维dp解决
物品重量:nums[1], nums[2],...,nums[n]
物品价值:nums[1], nums[2],...,nums[n]
背包容量:sum{num}/2
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for (int ele : nums) sum += ele;
if (sum % 2 != 0) return false;
int bag_weight = sum / 2;
vector<int> dp(bag_weight + 1, 0);
for (int i = 0; i < nums.size(); i++) {
for (int j = bag_weight; j >= nums[i]; j--) {
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
return dp[bag_weight] == bag_weight;
}
};