学习自:背包九讲
题目
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
基本思路:
完全背包和01背包的区别是一个物品可以被拿无限次,我们之前01背包是拿或者不拿的max,比较,然后我们处理完全背包的时候每个物品拿多少次就好了
每个物品最优拿法=max(不拿,拿一个,拿两个,...,拿n个),抛出个问题,n难道是无限吗?显然不是,大前提是背包总空间或者说处理到第i个物品时剩余空间有限
如果说当前空间最多只能拿n个,那我们的max函数就比较到这个物品拿n个时候就好了
这样一来,无限的问题被我们用背包总空间的限制条件处理成了一个动态的有限问题
其实也算是完全背包转多重背包
这样来就有了第一个递推式
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}
一个简单有效的优化
开始前可以来一次时间复杂度为O(n^2)的优化,就是假如有物品a,b
a比b占用空间多并且比b价值少,那我们可以直接排除掉不划算的物品a
对于随机生成的数据,这样子还是可以优化一部分的
转化为01背包问题求解
一个物品最多可以拿V/c[i]次,这一步把问题由完全背包转移到了多重背包
下面贴一张我自己写的文字的截屏
最优方法,时间复杂度O(n*V),空间复杂度为一维数组
使用了滚动数组,并且只是01背包代码的修改而已
回想一下我们学01背包滚动数组版本的时候,是不是要求数组第二维的j一定要从后往前,从大到小来遍历,就是为了防止新数据被新数据覆盖
我们只允许旧数据被新数据覆盖(拿一次还是不拿),那么新数据被新数据覆盖在实际层面怎么理解呢,就是(拿j次还是拿j-1次)
所以接下来我们在01背包代码的基础上让第二维的j从小到大,从前往后
如果说新数据覆盖新数据更好,那么我就给你覆盖,也就是如果已经拿了几个这种物品了,如果再拿几个会更好,那我就给你拿
for(ll i=1;i<=n;i++) for(ll j=c[i];j<=V;j++) { f[j]=max(f[j],f[j-c[i]]+w[i]); }
感谢老师教的这种层面理解方法,通俗易懂
原文地址:https://www.cnblogs.com/zyacmer/p/10011124.html