1. 背包必须填满
表示前i 件物品恰好装满容量j 的最大价值。
看来方程并没有改变,说明求出来的还是基础模型的结果,两者该怎么区分呢? 实际上,若
,表示前
个物品在空间为
的情况下的最大价值为0, 基础模型的话,这样做是可以的,可以表示一件物品都不能选,所以价值为0。但现在要求必须把
装满。一件物品都不选,却想把
的容量占满,是不合法的,而不应该让其等于0就草草 了事。
所以需要做以下调整:
- 初始化 数组为-1,表示不合法。
,表示什么都不选,在容量为 0的情况下,最大价值为0。
- 不合法的情况,是没有资格去更新其他值的。也就是如果
则不能更新
的值。
#include <bits/stdc++.h>
using namespace std;
int n,W,w[105],v[105],f[105][105];
int main(){
cin >> n >> W;
for(int i=1;i<=n;i++)
cin >> w[i] >> v[i];
memset(f,-1,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=W;j++){
f[i][j]=f[i-1][j];
if(j>=w[i] && f[i-1][j-w[i]]!=-1)
f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
}
}
cout << f[n][W];//n个物品,体积恰好为W时,最大价值
}
2. 至少背包填满
表示选前
种物品,体积至少为
,最小花费为
。 容量至少为
,言外之意,容量和为
都可以。那容量枚举到哪里才是上限呢?数组也不好定义大小吧? 当然一个解决思路是,最多枚举到
那时间复杂度和空间复杂度都会急剧增长。 01背包的特点是,每件物品有选和不选两种决策。那么
根据集合划分,可以分为:
全部由前
物品凑出
- 也可以选择 物品,那么前
个物品需要凑出至少为
,那么前
的贡献值范围可以是
,在这个范围内找到最小值即可,但这个范围的最小值必定在
处,利用常识贪心证明:同样是前
种物品,需要的体积值越大,花费趋势也越大。
关键在于 可以是负数,但前
个物品不可能凑一个负数,至少是0 ,而体积值为0 时,是不需要选任何物品的,花费为0 ,也即
。
#include <bits/stdc++.h>
using namespace std;
int n,w[105],v[105],W,f[105][105];
int main() {
cin >> n >> W;
for(int i=1; i<=n; i++)
cin >> w[i] >> v[i];
memset(f,0x3f,sizeof f);
f[0][0]=0;
for(int i=1; i<=n; i++) {
for(int j=0; j<=W; j++) {
int s=j-w[i];
s=max(0,s); //至少为0
f[i][j]=min(f[i-1][j],f[i-1][s]+v[i]);
}
}
cout << f[n][W];//前i个物品凑V的最小花费
}