背包问题之0-1背包(一)

来源:互联网 发布:美国经济数据一览表 编辑:程序博客网 时间:2024/05/21 13:59

问题描述:有n个物品,第i个物品的重量为w[i],价值为v[i]。选一些物品放入背包中,使背包内物品总重量不超过W的前提下,总价值尽量大。(所有值均小于1000)
题目链接:NYOJ题目289 苹果
解题思路:若设f[i][j]表示前i(1<=i<=n)个物品选择放入容量为j(0<=j<=n)的背包中能达到的最大总价值,则n个物品放入容量为W的背包中能达到的最大总价值f[n][w]即为所求。而每个物品只有放入和不放入背包中两种选择(这也是称之为0—1背包的原因,每个物品只能放入0个或1个),当第i个物品不放入容量为j的背包时,则容量为j的背包能达到的最大价值与前i-1个物品选择放入容量为j的背包时背包能达到的最大价值相等,即此时f[i][j]=f[i-1][j]。当第i个物品放入容量为j的背包时,则容量为j的背包能达到的最大价值等于前i-1个物品选择放入容量为j-w[i]的背包能达到的最大价值与w[i]之和,即f[i][j]=f[i-1][ j-w[i] ] + w[i]。
故:  if(j>=w[i])
        { f[i][j]=max( f[i-1][j] , f[i-1][ j-w[i] ]+w[i] ) }
      else
        { f[i][j]=f[i-1][j] }

源代码如下:

#include <stdio.h>#define maxn 1001int f[maxn][maxn];int main(){int n,W,w,v,i,j;while(scanf("%d%d",&n,&W)&&(n+W)){for(j=0;j<=W;j++) //对f[0][j]赋初值0f[0][j]=0;for(i=1;i<=n;i++){scanf("%d%d",&w,&v);for(j=0;j<=W;j++)   if(j>=w&&f[i-1][j-w]+v > f[i-1][j])   //只有当j>=w[i]时,w[i]才有放入的可能f[i][j]=f[i-1][j-w] + v;elsef[i][j]=f[i-1][j];}printf("%d\n",f[n][W]);}return 0;}

 

空间优化:滚动数组
优化思路:因为求f[i][j]时只用到f[i-1][j]和f[i-1][ j-w[i] ],且j<=j&&j-w[i]<=j,故f[i-1][j] 用过之后将其值改变也不会影响结果,所以可以用f[i-1][j]来存放f[i][j]的值,但此时必须按j从大到小依次求f[i][j]的值并存入f[i-1][j]中,因为求f[i][j]时要用到f[i][ j-w[i] ]的值且j-w[i] < j,故求f[i][j]前f[i-1][ j- w[i]]的值不能变。这样就可以用f[j]来代替f[i][j],由于每加入一个物品f[j]都要按j从大到小的顺序求一次,故f[j]被称为滚动数组。
源代码如下:

#include <stdio.h>#include <string.h>#define maxn 1001int f[maxn];int main(){int n,W,w,v,i,j;while(scanf("%d%d",&n,&W)&&(n+W)){memset(f,0,sizeof(f));          //对f[0][i]赋初值0for(i=1;i<=n;i++){scanf("%d%d",&w,&v);for(j=W;j>=w;j--)           //只有当j>=w[i]时,w[i]才有放入的可能if(f[j]<f[j-w] +v )f[j]=f[j-w] +v;}printf("%d\n",f[W]);}return 0;}


时间优化:提升j的下限值(只有当W很大时才有一定的效果)
优化思路:因为第n物品选择是否放入时只需求f[W],倒退到第n-1个物品只需要求f[ W-w[n] ] 和 f[W],即区间W-w[n]<=j<=w 上的f[j];倒退到第i个物品只需求区间W-w[n]-w[n-1]- .... -w[i+1]<=  j  <=W 上的f[j];故对于第i个物品j的下限为 W-w[n]-w[n-1]-  ... -w[i+1]即W-sum{ w[i+1 ..... n] },但是不要忘了只有当j>=w[i]才需要更改f[j]的值,即j原本下限为w[i];故j的下限downj=max{  W-sum{ w[i+1 ..... n] }  ,  w[j]  }。
源代码如下:

#include <stdio.h>#include <string.h>#define maxn 1001int w[maxn],v[maxn],f[maxn],sumi[maxn];  //sumi[i]用于存储sum{ w[1.....i] }int main(){int n,W,i,j,downj;while(scanf("%d%d",&n,&W)&&(n+W)){memset(f,0,sizeof(f));          //对f[0][i]赋初值0sumi[0]=0;for(i=1;i<=n;i++){scanf("%d%d",w+i,v+i);sumi[i]=sumi[i-1]+w[i];}for(i=1;i<=n;i++){downj=(W-sumi[n]+sumi[i-1]);if(downj<w[i])downj=w[i];for(j=W;j>=downj;j--)           if(f[j]<f[ j-w[i] ] + v[i] )f[j]=f[ j-w[i] ] + v[i] ;}printf("%d\n",f[W]);}return 0;}


 

 

原创粉丝点击