0-1背包和完全背包

来源:互联网 发布:linux oracle lsnrctl 编辑:程序博客网 时间:2024/06/05 09:39

一.0-1背包

1.0-1背包指的是每件物品要么取一次,要么不取,目的是找到装到背包里最大价值。c[N]代表物体重量,w[N]代表物体价值,V代表背包容量。


2.时间复杂度:O(N*V)   N指的是物品个数,V指的是背包的容量,这是最优情况,无法再优。


3.空间复杂度:原始:f[N+1][V+1]———>改进:f[V+1];


4.初始化问题:
  (1)如果没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为 0。

  (2)如果要求恰好装满背包,那么在初始化时除了f[0]为 0,其它f[1..V]均设为-∞,这样就可以保证最终得到的 f[N]是一种恰好装满背包的最优解。


5.0-1背包改进算法第二重循环为何逆序的分析

    for(i=0;i<N;i++)

        for(v=V;v>=c[i];v--)   //这里为何要逆序?

            f[v]=max(f[v],f[v-c[i]]+w[i]);              【1】



 

分析:由原始的状态转移方程可知当v>=c[i]时,f[i][v]=max{f[i-1][v-c[i]]+w[i],f[i-1][v]}。  【2】 

要保证第i次循环中的状态 f[i][v]是由状态f[i-1][v-c[i]]递推而来,即如何保证【1】f[v-c[i]]【2】中的f[i-1][v-c[i]]

 

为直观看出画图如下:


 

从图中可以看出,当第二重循环正序时,当扫到点3的时候,f[v-c[i]]的值并不是f[i-1][v-c[i]]

而是f[i][v-c[i]]。只有逆序的时候才可以做到要求的那样。

二.完全背包(只给出一种O(N*V)算法)

1.完全背包:各物品可以无限取。
2.时间复杂度:O(N*V)
3.空间复杂度:f[V+1];



三.0-1背包和完全背包的对比情况:

因为0-1背包每件物品只能取一次,而完全背包不限取的次数。

0-1背包:      f[i][v]=max{f[i-1][v-c[i]]+w[i] ,f[i-1][v]}

         要么取第i种物品,问题转化为前i-1种物品放入剩下的容量为v-c[i]的背包中。

         要么不取第i种物品,问题转化为前i-1种物品放入容量为v的背包中。


完全背包:            f[i][v]=max{f[i][v-c[i]]+w[i],f[i-1][v]}

         要么取第i种物品,问题转化为前i种物品放入剩余的容量为v-c[i]的背包中。

         要么不取第i种物品,问题转化为前i-1种物品放入容量为v的背包中。

对于完全背包用一维数组:

             for(i=0;i<N;i++)

           for(v=c[i];v<=V;v++)

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

这里的第二重循环必须得正序,因为只有正序才能保证max里面的f[v-c[i]]f[i][v-c[i]]

四.示例

w[4]={30,14,16,20}//各物品价值

c[4]={6,3,4,2}//各物品重量

设背包容量为V


五.代码清单

#include <stdio.h>#include <stdlib.h>#define MIN -32768int max(int a,int b){    return a>b?a:b;}//0-1背包int knapSack_01_Bas(int w[],int c[],int N,int V){    int f[N+1][V+1];    int i=0;    int v=0;    for(i=0;i<=N;i++)    {        for(v=0;v<=V;v++)        {             if(i==0||v==0)             f[i][v]=0;            else if(c[i-1]>v)                f[i][v]=f[i-1][v];            else            f[i][v]=max(f[i-1][v],f[i-1][v-c[i-1]]+w[i-1]);        }    }    if(f[N][V]<0)        f[N][V]=0;    return f[N][V];}int knapSack_01_Bas_fill(int w[],int c[],int N,int V){    int f[N+1][V+1];    int i=0;    int v=0;    for(i=0;i<=N;i++)    {        for(v=0;v<=V;v++)        {             if(i==0&&v!=0)             f[0][v]=MIN;             else if(v==0)             f[i][0]=0;            else if(c[i-1]>v)                f[i][v]=f[i-1][v];            else            f[i][v]=max(f[i-1][v],f[i-1][v-c[i-1]]+w[i-1]);        }    }    if(f[N][V]<0)        f[N][V]=0;    return f[N][V];}int knapSack_01_Pro(int w[],int c[],int N,int V){    int f[V+1];    int i=0;    int v=0;    /*    f[0]=0;    for(i=1;i<=V;i++)    {        f[i]=MIN;    }    */    for(i=0;i<=V;i++){        f[i]=0;    }    for(i=0;i<N;i++)        for(v=V;v>=c[i];v--)            f[v]=max(f[v],f[v-c[i]]+w[i]);    if(f[V]<0)        f[V]=0;    return f[V];}int knapSack_01_Pro_fill(int w[],int c[],int N,int V){    int f[V+1];    int i=0;    int v=0;    f[0]=0;    for(i=1;i<=V;i++)    {        f[i]=MIN;    }    /*    for(i=0;i<=V;i++){        f[i]=0;    }    */    for(i=0;i<N;i++)        for(v=V;v>=c[i];v--)            f[v]=max(f[v],f[v-c[i]]+w[i]);    if(f[V]<0)        f[V]=0;    return f[V];}//完全背包int knapSack_complete(int w[],int c[],int N,int V){    int f[V+1];    int i=0;    int v=0;     for(i=0;i<=V;i++){        f[i]=0;    }     for(i=0;i<N;i++)        for(v=c[i];v<=V;v++)            f[v]=max(f[v],f[v-c[i]]+w[i]);    if(f[V]<0)        f[V]=0;    return f[V];}int main(){      int w[4]={30,14,16,20};//价值      int c[4]={6,3,4,2};//重量      int V=15;//背包容量      int i=0;      for(i=0;i<=V;i++)      printf("Basic--V=%d,max value:%d\n",i,knapSack_01_Bas(w,c,4,i));            for(i=0;i<=V;i++)      printf("Basic fill knapSack--V=%d,max value:%d\n",i,knapSack_01_Bas_fill(w,c,4,i));      for(i=0;i<=V;i++)      printf("Pro--V=%d,max value:%d\n",i,knapSack_01_Pro(w,c,4,i));            for(i=0;i<=V;i++)      printf("Pro fill knapSack--V=%d,max value:%d\n",i,knapSack_01_Pro_fill(w,c,4,i));      for(i=0;i<=V;i++)      printf("Complete knapSack--V=%d,max value:%d\n",i,knapSack_complete(w,c,4,i));      return 0;}




0 0