01背包问题变形

1. 背包必须填满

f[i][j]表示前i 件物品恰好装满容量j 的最大价值。 f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i])看来方程并没有改变,说明求出来的还是基础模型的结果,两者该怎么区分呢? 实际上,若f[i-1][j-w[1]]==0 ,表示前 i-1个物品在空间为j-w[i]的情况下的最大价值为0, 基础模型的话,这样做是可以的,可以表示一件物品都不能选,所以价值为0。但现在要求必须把 j-w[i ]装满。一件物品都不选,却想把 j-w[i ] 的容量占满,是不合法的,而不应该让其等于0就草草 了事。

所以需要做以下调整:

  • 初始化 数组为-1,表示不合法。 f[0][0]=0,表示什么都不选,在容量为 0的情况下,最大价值为0。
  • 不合法的情况,是没有资格去更新其他值的。也就是如果f[i-1][j-w[i]]==1 则不能更新f[i][j] 的值。
#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. 至少背包填满

f[i][j]表示选前i 种物品,体积至少为 j,最小花费为 f[i][j]。 容量至少为j ,言外之意,容量和为 j,j+1,j+2...都可以。那容量枚举到哪里才是上限呢?数组也不好定义大小吧? 当然一个解决思路是,最多枚举到\sum_{i=1}^{n}(w[i]) 那时间复杂度和空间复杂度都会急剧增长。 01背包的特点是,每件物品有选和不选两种决策。那么f[i][j ] 根据集合划分,可以分为:

  • j全部由前i-1物品凑出
  • 也可以选择 物品,那么前i-1 个物品需要凑出至少为j-w[i] ,那么前i-1 的贡献值范围可以是[j-w[i],+\infty ] ,在这个范围内找到最小值即可,但这个范围的最小值必定在j-w[i ] 处,利用常识贪心证明:同样是前i 种物品,需要的体积值越大,花费趋势也越大。

关键在于j-w[i ] 可以是负数,但前i-1 个物品不可能凑一个负数,至少是0 ,而体积值为0 时,是不需要选任何物品的,花费为0 ,也即 f[i-1][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的最小花费
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值