文章目录
三种背包比较
01背包
N件物品,每件物品有价值V和消耗的容量C, 背包的容量有限,一个物品只能取一次,什么情况下价值最大?
完全背包
N件物品,每件物品有价值V和消耗的容量C, 背包容量有限,一个物品可以无限取多次,什么情况下价值最大?
多重背包 属于完全背包的一种
N件物品,每件物品有价值V和消耗的容量C, 背包容量有限,一个物品最多可以取M次,什么情况下价值最大?
‘组合背包’
N件物品,每件物品有价值V和消耗的容量C,背包容量有限,求装满背包的组合个数。
状态转移方程
01背包状态转移方程
f(v) = max(f(v), f(v - Ci) + Wi)
//其中f(v)表示容量为v的背包的最大价值
完全背包状态转移方程
f(v) = max{
f(v - k * Ci) + k * Wi| 0 <= k * Ci <= v}
// k表示物品可以被取0至多次
多重背包状态转移方程
f(v) = max{
f(v - k * Ci) + k * Wi | 0 <= k <= Mi}
状态转移方程其实好理解,通过例题来熟悉。
分类解题模板
首先是背包分类的模板:
- 1、0/1背包:
外循环nums,内循环target,target倒序且target>=nums[i];
- 2、完全背包:
外循环nums,内循环target,target正序且target>=nums[i];
- 3、组合背包:
外循环target,内循环nums,target倒序且target>=nums[i];(倒序正序需要看01还是完全)
- 4、分组背包:
这个比较特殊,需要三重循环:外循环背包bags,内部两层循环根据题目的要求转化为1,2,3三种背包类型的模板
然后是问题分类的模板:
- 1、最值问题:
dp[i] = max/min(dp[i], dp[i-nums]+1)或dp[i] = max/min(dp[i], dp[i-num]+nums);
- 2、存在问题(bool):
dp[i]=dp[i]||dp[i-num];
- 3、组合问题:
dp[i]+=dp[i-num];
例题
分割等和子集
链接🔗
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
通过判断是否可以正好组成容量为1/2sum的背包来判断是否可以划分子数组。
01背包, 外层num, 内target, 逆序
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(const int & i: nums)sum += i;
if(sum%2) return false;
sum /= 2;
// 01 背包
vector<bool> dp(sum+1, false);
dp[0] = true;
for(int i = 0; i < nums.size(); i++){
for(int j = sum; j >= nums[i]; j--){
dp[j] = dp[j] || dp[j - nums[i]];
}
}
return dp[sum];
}
};
零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
简单的例子,完全背包,外层num,内层target, 顺序
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int>f(amount + 1, INT_MAX);
f[