01背包与完全背包:正序Or倒叙遍历背包数究竟什么区别
第一次写, 真的菜鸡的感性理解,如有理解错误之处,希望评论区多多指导
刚开始学背包问题,虽然背代码很容易,但是着实蒙蔽
此篇小文希望给新手一些帮助,放代码!
//01背包问题
for(int i=1;i<=n;i++)
{
int v,w;
cin>>v>>w;//边输入边处理
for(int j=m;j>=v;j--)//倒叙遍历背包数
f[j]=max(f[j],f[j-v]+w);
}
//完全背包问题
for(int i=1;i<=n;i++)
{
int v,w;
cin>>v>>w;
for(int j=v;j<=m;j++)//正序遍历背包数
{
f[j]=max(f[j],f[j-v]+w);
}
}
对比一下,确实区别就在一个倒序,一个正序
解决01背包问题时倒叙遍历背包数,是因为01背包问题每种物品只能装一次
如果正序遍历,那么当dp[大的背包数]更新:f[j]=max(f[j],f[j-v]+w)时,
因为dp[小的背包数]已经更新过了,且更新时dp[小的背包数]会影响dp[大的背包数]
原来的dp数组已经不是单纯的前i-1种物品更新的结果
换句话说,给dp[小的背包数]更新已经相当于装入一次此种类物品,
而dp[大的背包数] 更新时本身已经代表着装入一次此种物品,但由于用到dp[小的背包数],又装入了一次此种物品
不符合01背包问题只装入一次的设定
而这种情况恰恰时完全背包问题所期望的
所以解决完全背包问题时,我们采用正序遍历
那么再来看看倒序遍历
因为dp[大的背包数]并不会影响到dp[小的背包数]
所以更新每个背包数时,dp数组都是上一个循环产生的旧数组
代表的是仅仅是前i-1个物品装入更新的结果
所以相当于每次装入只是第一次装入此种物品
符合01背包只装入一次的设定
多重背包
数据范围较小的情况下,都可以将具有多件的物品拆分开来,转化为01背包问题
易知
- 任何数=1+1+1…+1
- 任何数==2^ 0x0+2^ 1x1+…+2^n*xn+剩余
所以对应数据大小我们有两种处理方法- 死拆
- 二进制拆分
int t=0;
while(N--)//转化为01背包
{
int a,b,c;
cin>>a>>b>>c;
while(c--)
{
v[t]=a;
w[t++]=b;
}
}
//二分拆分
int cnt=0;//组别
while(N--)
{
int a,b,c;//该种物品的单位体积,单位价值,总件数
cin>>a>>b>>c;
int k=1;//一组内的件数
//1 2 4 8 16...
while(k<=s)
{
v[++cnt]=a*k;//该组的体积
w[cnt]=b*k;//价值
s-=k;//剩余件数
k*=2;//下一组的件数
}
if(s>0)//如果还有没分完的
{
v[++cnt]=a*s;
w[cnt]=b*s;
}
}