1.背包问题
#include <iostream>
#include <vector>
using namespace std;
int wei_bag_prob() {
vector<int> weight = { 1, 3, 4 }; // 物品的重量
vector<int> value = { 15, 20, 30 }; // 物品的价值
int bagweight = 4; // 背包承重
// 1.确定dp是二维数组的 以及dp[i][j]代表了 重量为0-i重量的物品 装在承重为j的背包中的最大价值
vector<vector<int>> dp(weight.size(), vector<int>(bagweight+1, 0));
// 2. 确定递推公式
// 如果当前的物品重量weight[i] 大于背包承重j, 则dp[i][j] = dp[i-1][j]
// 否则 dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]);
// 3. 初始化数组
for (int n = weight[0]; n <= bagweight; n++) {
dp[0][n] = value[0];
}
for (int i = 1; i < weight.size(); i++) {
for (int j = 0; 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[weight.size() - 1][bagweight];
}
int main() {
int result = wei_bag_prob();
cout << result << endl;
return 0;
}
2. 一维01背包
- 为什么一维是必须先遍历物品再遍历背包, 而二维的是什么顺序都可以
- 先遍历物品的方式可以将 第i-1个物品的状态先保留下来,供第i个物品做参考
- 为什么一维的背包重量必须是倒序遍历
- dp[j] = dp[j-weight]+value[i] dp[1] = dp[1-1] + 15 = 15; dp[2] = dp[2-1]+15 = 30 正序遍历会导致有的value重复加两次
- 倒叙dp[2] = dp[2-1] + 15 = 15 dp[1] = dp[1-1] + 15 = 15;
void test_1_wei_bag_problem() {
vector<int> weight = { 1, 3, 4 }; // 物品的重量
vector<int> value = { 15, 20, 30 }; // 物品的价值
int bagweight = 4;
// 1.确定dp数组 并且初始化
vector<int> dp(bagweight + 1, 0);
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]);
}
}
cout << dp[bagweight] << endl;
}
3. 分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
if(sum % 2 == 1) return false;
int target = sum / 2;
sort(nums.begin(), nums.end());
// 找出数组中是否有元素的和为 num
// 使用背包问题的思想, nums中的元素为物品的value, 背包承重为sum/2
// 背包的体积为sum / 2
// 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
// 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
// 背包中每一个元素是不可重复放入。
// 1.dp数组中的; dp[j]表示背包总重量是j;初始化直接全部为0
vector<int> dp(target+1, 0);
// 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;
}
};