背包算法学习笔记

主要学习的是背包九讲中的内容:

0-1背包问题

0-1背包问题,是讲N件物品放入背包(体积为V),每种物品一件,只能选择放或者不放,体积和价值分别为c[i]和v[i]。(cost花费,value价值)

计算公式为F(i,v) = max(F(i-1,v),F(i-1,v-c[i]) + v[i])

 

一、初步考虑定义F[N][V],F[i][j]为在体积为j的情况下,考虑i件物品时的最大价值。伪代码:

FOR[0][0...V]<-0

for i:1->N

    for j:c[i]->V

        F[i][j] = max(F[i-1][j],F[i-1][j-c[i]]+v[i]);

时间复杂度为O(n^2),空间复杂度为O(N*V)

 

二、进一步考虑节省空间,定义F[V],将N的循环结果覆盖,为防止每次循环前面的计算被叠加到后面,采用从后往前的计算方式。伪代码:

FOR[0...V]<-0

for i:1->N

    for j:V->c[i]

        F[j] = max(F[j],F[j-c[i]]+v[i]);

时间复杂度为O(n^2),空间复杂度为O(V)

 

三、继续考虑节省时间,定义F[V],同上。伪代码:

FOR[0...V]<-0

for i:1->N

    for j:V->max(c[i],V-Σ(v:i->N)c[v])

        F[j] = max(F[j],F[j-c[i]]+v[i]);

时间复杂度为O(n^2),空间复杂度为O(V)

 

四、特殊要求

如果要求正好装满背包,则将上面中的初始化赋值时,只有FOR[0][0]或FOR[0]赋值为0,其他全部赋值为-∞。这样,只有每次正好装满的时,才会在下一次选择中脱颖而出,当然,如果没有正好装满的机会则会一直讲-∞向下传递,甚至最终答案也是他,那就说明无法得到答案。

 

五、总结

可以清楚看出,上面有两次时间优化,一个是体积从c[i]开始,一个是从V-Σ(v:i->N)c[v]开始。小于两个中的任意一个其实都不用再考虑了。

c[i]比较好考虑,因为小于c[i]说明他本身就没有装下一个i好物体的能力,所以需要考虑了。

第二个是因为如果体积小于V-Σ(v:i->N)c[v]说明剩余的体积已经大于剩余的物体的全部体积了,再向前计算已经不可能有更高的价值了,所以也不需要计算了。

0-1背包算法时背包算法的基础,可以说,每一个考虑的问题都具有普适性,为接下来更深入的问题做铺垫。

总结单件物品计算函数,后面会用到,节约时间。至于为什么算则第二中算法而不是第三种算法,完全背包的第三种算法有解释。

def ZeroOnePack(F,C,V)

for v:V->C

    F[v] = max(F[v],F[v-C] + V);

 

完全背包问题

完全背包问题没有了物品的数量限制,可以任意的放入0~n件,数量不限。

一、直接从基础方程式转化

设定k为一种物品放入的数量,为变量。则方程为:

F(i,v) = max(F(i-1,v),F(i-1,v-kc[i]) + kv[i]|0<=k*c[i]<=v[i])

时间复杂度为O(NVΣ(v[i]/c[i])),过高

 

二、转化成0-1背包问题

利用二进制思想,将第i种物品转化成本为c[i]*2^k,价值为v[i]*2^k的若干件物品。任意一个数字都可以用二进制组合表示,这样物品就可以转成0-1问题的使用方式,放或者不放。

这样问题的时间复杂度就为O(NVΣ(log(v[i]/c[i]))),是一个很大的改进,但仍然太高;

 

三、O(VN)算法

直接放代码:

FOR[0...V]<-0

for i:1->N

    for j:c[i]->V

        F[j] = max(F[j],F[j-c[i]]+v[i]);

时间复杂度为O(n^2),空间复杂度为O(V)

与0-1背包的第二种算法相比,只有第二层循环的顺序颠倒了。可以思考一下,当时为什么选择从后往前。就是为了消除前面的计算会累加到后面,所以从前往后就不会了。

为何不采用0-1算法的第三种算法扩展,因为这一次使用数量不再限制,所以不会出现剩余体积大于剩余所有物品的体积的情况。就是说他只适用与0-1背包,不适合扩展后的背包问题。

该方法其实可以调换循环层次的顺序。

 

四、总结

总共考虑了两个比较有意思的问题,一个是二进制思想,一个是第三种算法。

二进制思想是一个很好的解决大量无用遍历的方式,快排上的时间缩减也和他有同源的思想。如果物品的体积较大,完全可以采用二进制思想整合一下第三种算法,也可以再缩减一次时间复杂度。

总结单件物品计算函数。

def CompletePack(F,C,V)

for v:C->CV

    F[v] = max(F[v],F[v-C] + V);

 

多重背包问题

 

猜你喜欢

转载自blog.csdn.net/astar3/article/details/81980665