背包算是dp里面比较简单的一类,因为每个背包的类型都有他固定的模板,所以就比较好做。
背包分为好几种背包,我所了解的就只有这4种:
1、0/1背包
2、完全背包
3、多重背包
4、二维费用背包
0/1背包
0/1背包应该算是背包的基础了。关于0/1背包的叙述应该是这样的,给定一个背包可以装M重量的东西,有n个东西,每个东西都有它的重量w,和他的价值c。求能装的最大价值。
我们这种题的 dp数组的设置 dp[i][j] i代表物品个数,j代表背包容量,他的值代表价值。这个题看了几个视频感觉画一个表格来做就比较明显。
这样看来dp方程就是
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
因为这里面的第一维的数组仅仅只第i和第i-1的比较,所以我们在这里用滚动数组来做就会节省空间
所以最终的dp方程就是
dp[j]=max(dp[j],dp[j-w[i]]+c[i]);
来对比一下这两个数组的伪代码
for(int i=1;i<=n;i++)
for(int j=w[i];j<=m;j++)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+c[i]);
这两个伪代码中第一个很好理解,第二个唯一不理解的地方莫过于第二个循环为啥是倒着循环。原因是我们使用滚动数组比较上一个和这个的大小倒着来就可以表示第i-1个的。
其实0/1还有好多变形在这儿就不一一说了。
完全背包
完全背包其实是按照0/1背包的基础上改的。在0/1背包中所的单品都是一个的,但是在完全背包中的所有的单品是不限的,最后求能够获得的最大价值。那么既然某个单品我们可以拿无限个那么在第i个物品中我们可以选择拿的个数就可以是0个、1个、2个等等一直到q个(q=背包的容量/第i个单品所占的容量),那么这个和0/1背包的联系就显而易见了,就是再加上这一种从0个到q个的情况就行了,那么这个的伪代码就是
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--) 这个为啥是倒叙的上面的0/1背包中说过了
for(int k=0;k<=j/w[i];k++) 这个循环就是来判断第i个数需要几个的情况
dp[j]=max(dp[j],dp[j-k*w[i]]+k*c[i]); 这个就是为了求出最终的结果
但是这种好理解的情况就有一种弊端,就是3重循环时间复杂度比较高很容易就超时。所以就有了他的简化形式。
for(int i=1;i<=n;i++)
for(int j=arr[i];j<=m;j++)
dp[j]=max(dp[j],dp[j-w[i]]+c[i]);
这个的简化形式和0/1背包的有点相似,唯一的区别就是第二个循环是正向的。
多重背包
多重背包其实也是和0/1背包给变型而来的,0/1背包每个单品只有一个,完全背包每个单品有无限个。那么多重背包的一个单品有一个确定的个数。同样也是求总价值最大。
因为这个单品的个数是一定的,我们其实可以把单品的个数可以继续分成一个一个的单品,那个我们就直接套用0/1背包的公式来进行解题就行了。
我们也可以根据完全背包的来给多重背包在进行一个循环。循环的终点是单品的个数和(背包的容量/第i个单品所占的容量)的较小量。
伪代码为:
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
for(int k=0;k<=s[i]&&j>=k*v[i];k++)s[i]为每个单品的个数
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
但是这个代码还是有一点繁琐。那么应该怎么简化他呢?那就涉及了多重背包二进制的优化。
二进制优化就是将第i个物品拆分成若干个物品,每个物品的体积和价值乘以一个拆分计数(1,2,4,8,16·····)就转换成0/1背包来进行求解
int num=1;//拆分计数
for(int i=1;i<=n;i++)
{
for(int j=1;j<=s[i];j*=2)
{存体积和价值
vv[num]=j*v[i],ww[num]=j*w[i];
num++;s[i]-=j;剩下的。
}
if(s[i])
{
vv[num]=s[i]*v[i];
ww[num]=s[i]*w[i];}
}
拆分完之后在使用0/1背包的代码即可
for(int i=1;i<num;i++)
for(int j=m;j>=vv[i];j--)
dp[j]=max(dp[j],dp[j-vv[i]]+ww[i]);
二维费用背包
省赛的那个题应该就是二维费用背包,当时没做出来。二维费用背包还不是很理解,下一次在补上吧。
这一周的周结就先到这儿,这一次的作业没有做完,原因有这两方面。省赛结束之后自己确实变懒了,题目一个题卡壳很长时间,就导致到最后就做不完了,第二就是在摸索这个背包的时候还是有点不理解,比如那种情况是dp[j]+=dp[j-w[i]]+arr[i];啥时候求max。所以进度就一直赶不上,说到底还是花的时间太少了,就算结束了这个专题我也会想办法补上的,不能让自己仅仅学到的知识,学不明白吧!