前两天学了基础的背包的板子,但是发现不能满足一些题目的要求或者是空间复杂度过大
那有没有更加节约时间空间的写法呢?有的有的,我们先从多重背包问题引入吧
问题描述
有 NN 件物品和一个体积为 MM 的背包。第 ii 个物品的体积为 vivi,价值为 wiwi。每件物品只能使用 cici 次。
请问可以通过什么样的方式选择物品,使得物品总体积不超过 MM 的情况下总价值最大,输出这个最大价值即可。
输入格式
第一行输入两个正整数 N,MN,M。(1≤N,M≤100)(1≤N,M≤100)
接下来 NN 行,每行输入三个整数 vi,wi,civi,wi,ci。(0≤vi,wi,ci≤100)(0≤vi,wi,ci≤100)
输出格式
输出一个整数,表示符合题目要求的最大价值。
样例输入
4 5
1 2 3
2 4 1
3 4 3
4 5 2
样例输出
10
这个问题的数据量比较小,我们可以用完全背包的写法来ac这题
代码如下
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
dp[i][j]=dp[i-1][j];
for(int l=1;l<=c[i];l++)
if(j>=v[i]*l)
dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]*l]+l*w[i]);
}
但是当数据量变大到
1≤N,M≤2000
1≤vi,wi,ci≤2000
时,我们会得到一个tle,甚至是mle
因为每件物品的数量可能会非常大,所以我们需要对数量进行拆分组合,有没有一种能组成任意数字的一种计算工具呢?有的有的,那就是二进制,我们对每个物品进行二进制拆分,使之被分为1,2,4,8,...的组合,这些组合能够组合成任意数字
得到以下读入拆分的代码
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i]>>c[i];
for(int x=1;c[i]>0;x*=2)
{
int times=(min(x,c[i]));
weight.push_back(v[i]*times);
value.push_back(w[i]*times);
c[i]-=times;
}
}
二进制拆分使每个物品消耗的时间从n变为logn
对于空间,我们可以使用一维数组进行dp的记录
不难观察到二维数组中状态转移方程中dp数组的变化只是第一个下标沿用上一层,也就是dp[i][j]基]本沿用dp[i-1][j],所以可以得出每一层只和上一层有关,于是我们可以简化dp数组为一层,并且为了不影响装不下第i个物品时的情况,所以我们应该逆序遍历,得到如下代码
vector<int>dp(m+1);
for(int i=0;i<weight.size();i++)
for(int j=m;j>=weight[i];j--)
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
于是我们就得到了多重dp的解
762

被折叠的 条评论
为什么被折叠?



