动态规划之完全背包详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划之完全背包详解相关的知识,希望对你有一定的参考价值。

在昨天我已经很详细的讲解过01背包的动态规划问题了,今天我讲解的是完全背包的问题,这是01背包的详解:http://www.cnblogs.com/Kalix/p/7617856.html

先看问题:在n种物品中选取若干件(同一种物品可多次选取)放在空间为v的背包里,每种物品的体积为c1,c2,…,cn,与之相对应的价值为w1,w2,…,wn.求解怎么装物品可使背包里物品总价值最大

看完这个问题,你也许会觉得这个不就是01背包的升级版吗,其实就是这样,完全背包问题与01背包问题的区别在于完全背包每一件物品的数量都有无限个,而01背包每件物品数量只有1个

所以说与它相关的策略已经不是只有取和不取这两种策略了,而是有取0件、取1件、取2件……等等很多种策略

如果我们用和01背包一样的状态,f[i][v]表示前i种物品恰放入一个容量为v的背包的最大价值,那我们应该用k表示当前容量下可以装第i种物品的件数,那么k的范围应该是0≤k≤v/c[i],

既然要用当前物品i把当前容量装满,那需要0≤k≤v/c[i]件,其中k表示件数。

下面给出状态转移方程:

f[i][j] = max{f[i-1][v],f[i-1][v - k * c[i]] + k * w[i]}(0<=k*c[i]<=v)

 

贴一段代码:

 for (int i = 1; i < n; i++){
   for (int j = 1; j <= v; j++){
     for (int k = 0; k*c[i] <= j; k++){
    if(c[i]<=j)/*如果能放下*/
    f[i][j] = max{f[i-1][j],f[i-1][j - k * c[i]] + k * w[i]};/*要么不取,要么取0件、取1件、取2件……取k件*/
    else/*放不下的话*/
    f[i][j]=f[i-1][j]/*继承前i个物品在当前空间大小时的价值*/
        }
    }    
}

我们可以对其进行优化:如果有两件物品a、b满足c[a]<=c[b]且w[a]>=w[b],则将物品b去掉,不用考虑。因为你可以用 占用体积小的物品 得到 比 占用体积大的物品还要多的价值,何乐而不为呢。其实对于完全背包,可以再优化,首先将容量大于v的物品去掉,然后排序计算出容量相同的物品中价值最高的是哪个,我们只要价值大的就可以了。

画一个v=6,c[1]=1 , w[1]=3 ; c[2]=3 , w[2]=10的表格

 i\\j

j=0 j=1 j=2 j=3 j=4 j=5 j=6

i=0

0 0 0 0 0 0 0

i=1

0 3 6 9 12 15 18

i=2

0 3 6 10 13 16 20

我们再进行优化,改变一下dp思路,让f[i][j]表示出在前i种物品中选取若干件物品放入容量为j的背包所得的最大价值。

所以说,对于第i件物品有放或不放两种情况,而放的情况里又分为放1件、2件、......v/c[i]件

如果不放那么f[i][j]=f[i-1][j];如果确定放,那么当前背包中应该出现至少一件第i种物品,所以f[i][j]中至少应该出现一件第i种物品,即f[i][j]=f[i][j-c[i]]+w[i],为什么会是f[i][j-c[i]]+w[i]?

因为我们要把当前物品i放入包内,因为物品i可以无限使用,所以要用f[i][j-c[i]];如果我们用的是f[i-1][j-c[i]],f[i-1][j-c[i]]的意思是说,我们只有一件当前物品i,所以我们在放入物品i的时候需要考虑到第i-1个物品的价值(f[i-1][j-c[i]]);但是现在我们有无限件当前物品i,我们不用再考虑第i-1个物品了,我们所要考虑的是在当前容量下是否再装入一个物品i,而[j-c[i]]的意思是指要确保f[i][j]至少有一件第i件物品,所以要预留c[i]的空间来存放一件第i种物品。总而言之,如果放当前物品i的话,它的状态就是它自己"i",而不是上一个"i-1"。

所以说状态转移方程为:

f[i][j]=max(f[i-1][j],f[i][j-c[i]]+w[i])

贴一段代码:

for(int i = 0 ; i < n ; i ++)  
{  
    for(int j = 1 ; j <= v ; j++)  
    {
        if(c[i]<=j)
        f[i][j] = max(f[i-1][j],f[i][j-c[i]]+w[i]);  
        else
        f[i][j]=f[i-1][j];
    }
}

  我们可以继续优化此算法,可以用一维数组写

我们用f[j]表示当前可用体积j的价值,我们可以得到和01背包一样的递推式:

f[j] = max(f[j],f[j-c[i]]+w[i])

先贴出代码,再讲解:

for(int i = 0 ; i < N ; i ++)  
{  
    for(int j = c[i] ; j <= V ; j++) 
     {
        if(c[i]<j)
        f[j] = max(f[j],f[j-c[i]]+w[i]); 
        else
        f[j]=f[j];

  } }

  

对于01背包来说,是逆序,装不装当前物品i取决于第i-1个物品(f[i][]只和f[i-1][]有关);而对于完全背包来说,装不装物品取决于他前一个物品i(因为可以放无数个物品i),因为当前物品i是无限的不用去考虑f[i-1][],而应当考虑当前状态下是否应当再装入当前一个物品i。或者换一种说法:完全背包考虑的是第i种物品的出现的问题(该不该放入当前物品i),第i种物品一旦出现它势必应该对第i种物品还没出现的各状态造成影响,也就是说,原来没有第i种物品的情况下可能有一个最优解,现在第i种物品出现了,而它的加入有可能得到更优解,所以之前的状态需要进行改变,故需要正序。

也就是说完全背包是说,在当前体积下,是否要放入或者再放入一个当前物品i,而01背包是说,在当前体积下,是否要放入一个当前物品i

对于完全背包还可以用二进制的方法写,这里就不讲了

谢谢

本文章原创,未经我允许不得转载

Authentic Author : Tranx

2017.10.2  19:18

 










以上是关于动态规划之完全背包详解的主要内容,如果未能解决你的问题,请参考以下文章

完全背包详解_动态规划

动态规划-第二节:动态规划之背包类型问题

动态规划之背包问题-01背包+完全背包+多重背包

动态规划之01背包详解解题报告

动态规划之——完全背包问题

动态规划之完全背包问题