瞎说一波3种基本背包问题【希望巨巨们指出错误】
0/1背包;
这是自己接触最早的背包,其实说0和1背包是最早。
0和1背包:
他很自由,价值和重量成比例,像一块豆腐你想对某一块拿多少就切多少,那么很明显处理方案就是按照物品价值和重量的比值排序,也可以说是效率吧,然后从大到小按照题目意思取就好了。不多说,继续。
0/1背包:
从字面意思就是对某个物体(只有一件)取或者不取,光光这个算法,就有很多很多长篇大论去讲述。我谈谈感受,其实DP非常有的一个特点就是递推性,然后各种每一步的无后效性和最优性一起来以后,就是非常完美的一个算法。让我感觉最深的就是递推性了。
0/1背包正文,对于此类背包问题,状态的变更只有在取不取这个上面,其实我不是很想直接把自己和读者强行拉进代码里来,但是既然是DP,我们也只能用DP的思想,基本上就是递推,或者记忆化搜索,递推那么肯定是一个一个递推(这句话有差池,对于多重背包),对于问题的本意,就是要求我们在所有(暂且有n个)的物品里怎么取是价值最大的,那么我保证前n-1个是最大了,那么就很明确,我第n个取了?在不超过背包重量的前提下,变大,那么取了,否则舍弃。我们可以用dp[i][j]代表前i个物品j重量的时候代表所取的价值最大。这个时候,dp[i][j]其实完全依赖于dp[i-1][]这个上面,所以萌新认为,倒推和正推没有任何差别。 dp[i[j]=max(dp[i][j],dp[i-1][j-w[i]]+val[i]);(val[i],w[i]分别代表某物品的价值和重量)。
插一句:
萌新现在的想法就是,应该对于DP问题,一开始不要急于求成搞出一个很妙的递推式。应该根据题目所需,对于每个分量都设一维看看,我想不会有很多题目升到4维上,然后进而可以进行空间上的优化,时间上也可以优化,慢慢地递推,会产生更多的思考,然后分类讨论或者强行暴力。。各种方法是会有的,没有那就是姿势不够啊。
好,讲一下0/1背包的优化,对于dp[i][j],我们很清楚地看到他是完全基于dp[i-1][]上的,那么我们可以开一个滚动数组,摆来摆去,也可以啊。或者到一维上,这就很能展示一个无后效性的问题,这个时候我们只能是倒推这个二维重量,当我们处理过前i-1个物品的时候,我们更新i时,以重量从大到小过来,那么被更新的就是比前i-1个里面他重量小的为基础+他的价值,如果重量从小到大更新,那么第i个小重量被更新以后,在更新大重量的时候调用的小重量是前i个的价值啊。OK。(看不懂其实没关系,后面完全背包会讲到。。后面讲的清楚一点。。。)
以代码理解(这里不是完整的,仅是更新重量的操作)
for(int j=W;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
刚刚被二维完全背包搞得脑子死机,所以心血来潮给自己总结一下,其实文字上下肯定很多错误,也请巨巨们直言指出qaq,真的非常感谢指出的巨巨。
OK,继续。
完全背包:
问题变成了,在0/1背包问题的基础上,现在一个物品是可以取无限个的。
我们可以先看看0/1背包问题省去一维的算法,我们发现他是倒推的,是啊,因为如果正推就会造成第i个物品在某一时刻被取了,因为dp值会变大,那么好巧啊,在某一刻我那个j-w[i]就等于了刚刚被更新(取了该物品)的重量,好已累加,这个i物品就被取了两次了,好,更巧,后面又有一个重量类似这样被更新,如果再巧一点,这个物品就要被取无数次了,这就不满足0/1背包了,等等,你刚刚说无限次,哎呀,完全背包正好是无限次,那么正的递推正好满足啊。对吼,就是这样,完全背包就是正的推的写法。= =、瞎瘠薄这样讲。。。
多重背包
以前无意中看到二进制的利用有树状数组和多重背包,那时候已经会了树状数组,对树状数组很喜欢啊,所以那时候对多重背包还是有点望而生畏的。
写了差不多10几发背包题了吧,最基础的就是
①求背包承重下的价值最大,还有
③物品的价值和重量是同一个值求在承重下的价值最大
③求一个最接近值(只要知道背包承重上限还是简单)
④求一个组成值且要求物品数量最小,物品计数,这里是要搞一个路径。
哦哦,这里要讲下多重背包啊,其实吧,讲起来就几句话,大致的意思(这里有点不求甚解的意思)就是,我本来是每种物品有num[i]种,现在我把每种物品用二进制的形式组合成一个个新物品,比如我有9个i物品,我会组成1,2,4,8,1这个五种物品,其实更朴素一点,我可以搞成1-9这9个物品,然后就是0/1背包模式,但是如果num[i]很大呢,复杂度不行啊,然而拆分成二进制,正好可以降低复杂度,更重要的是他在更新中其实已经涵盖了刚刚那个朴素处理的所有情况,因为二进制拆分出来的数可以组成在那个范围的任何数啊。
OK,没啥好说了,还有很多背包问题,比如分组的背包问题,二维费用背包,一定要好好看背包九讲。