(一)
依然是按照如下图进行分析
其实关键就是怎么划分集合
我们来注意现在的条件,每个物品都可以无限取
所以我们在划分集合的时候,要保证所有情况都能遍历过
所以第i种物品取1~k个的情况都要包含
注意:
现在我们的f[i][j]是由在f[i][j-k*v[i]]+w[i] 这k个选项中更新来的,对第i层的更新
是从1~k个不同的选择 去取其中的最大值
而01背包是从上一个,即f[i-1][j]去更新的,是对第i-1层的更新
不妨这样来划分集合
于是我们得到以下代码
此时的f[i][j]就是取f[i][j-k*v[i]]+k*w[i]的最大值
就是寻找在体积是j-v[i]的状态下价值的最大值
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int f[N][N];
int v[N],w[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k*v[i]<=j;k++)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
printf("%d",f[n][m]);
return 0;
}
(二)优化
我们看到三层循环太慢了,所以做优化
依然是从集合划分去看 我们看f[i][j]是怎么更新的
那现在先来拆解代码
k层循环去除后
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
if(j-v[i]>=0) f[i][j]=max(f[i][j],f[i][j-v[i])
else f[i][j]=f[i-1][j];
}
因为现在我们的f[i][j]是由在f[i][j-k*v[i]]+w[i] 这k个选项中更新来的
如此仿照将其变成一维的
现在我们的f[i][j]是由在f[i][j-k*v[i]]+w[i] 这k个选项中更新来的,对第i层的更新
所以就遍历的时候就不用担心j-k*v[i]先于j 导致与我们初始对f[i][j]的定义产生矛盾
即保证了逻辑的合理性
现在代码就是
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];//最大值
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
for(int i = 1 ; i<=n ;i++)
for(int j = v[i] ; j<=m ;j++)
{
f[j] = max(f[j],f[j-v[i]]+w[i]);
}
cout<<f[m];
return 0;
}
其实这个代码和01背包很像,区别就在于j是否要逆序遍历
而决定这一点的就是我们对于集合的划分方式,对f[i][j]的定义
题目来源acwing
模板来源:闫学灿
链接:https://www.acwing.com/activity/content/code/content/57785/
来源:AcWing