0-1背包问题

来源:互联网 发布:联合国统计数据库 编辑:程序博客网 时间:2024/05/22 15:15
问题描述:
 给了n种物品和一个背包。物品的重量的W[i](weight),价V[i](value),背包容量为c(capacity),问应该如何选择放入背包中的物品,使得装入背包的物品总价值最大。

每个物品只有两种可能的选择,放或者不放,所以称为0-1背包问题,
即要找到一个数组(x1,x2,……,Xn),x[i]∈{0,1},不放就是0,放入就是1

要求就是,
  ∑x[i]*w[i]<=c  1→n
  且,价值∑x[i]*v[i]最大


先看0-1背包问题是否满足最优子结构性质:
  设(y1,y2,y3,……Yn)是所给的0-1背包问题的一个最优解。
  那么(y2,y3,……Yn)必须是

 ∑y[i]*w[i]<=c - w[1]y1   2→n   
 且,价值∑y[i]*v[i]最大

 如果w[1]=0相当于没装,就是转换成2→n的物品放到容量c背包中,如果等于1,则该子问题就变成了去掉第   一个物品重量的背包问题

最优子结构的证明基本都是使用反证法来证明。
  如果(y2,y3,……Yn)不是上述子问题的一个最优解,那么假设最优解为
  (z2,z3,……Zn)
  那么
 ∑z[i]*v[i]才是该子问题的求出来的最大价值,
 那么原问题,最优解,就变成了:
  x[1]*v[1] + ∑z[i]*v[i]2→n

 那么得到的这个价值肯定比原先假设的最优解的价值还要大,很明显,这是与假设有矛盾的。
  so,(y2,y3,……Yn)必须是0-1背包子问题的一个最优解!!

然后,就是递归关系了

  m[i][j]  代表 i→n物品,背包容量为j,0-1背包问题的最优解
  w[i]重量 v[i]价值c背包容量

一.j>=w[i]剩下的背包容量可以放下
   不放第i个物品:转成  ,i+1→物品放入容量为j的背包中的最大价值      
   放第i个物品:转成,   i+1→物品放入容量为j-w[i]的背包中的最大价值+v[i].

  j<w[i]
  背包剩余的容量已经放不下了
   则:m[i][j]= m[i+1][j]

二.只有第n个物品的时候
  m[n][j] = v[n]  能放下    j>=w[n]
          0     放不下了  0<j<w[n]
 
 然后就是具体的实现过程

 最后得到的m[1][c]就是所要求的最优值。

举个具体例子:
n=5,c=10,w={2,2,6,5,4},v={6,3,5,4,6}。
0-1背包问题


代码:


C++语言:
#include<iostream>
usingnamespacestd;
intMAX(int a , int b)
{
   returna>b?a:b;
}
template<classType>
voidKnapsack(Type *v,int *w,int c,int n,int **m);

template<classType>
voidTraceback(Type **m ,int *w,int *x,intn,int c);

intmain()
{
   int n;  //物品个数
   int c;   //背包容量
   int i,j;
   
   cout<<"请输入物品个数:";
   cin>>n;
   cout<<"请输入背包容量:";
   cin>>c;
   
   int *w = new int[n+1]; //每个物品的重量。w[0]什么都不存,从1到n计算
   int *v = new int[n+1];  //价值
   int *x = new int[n+1];   //用于存放入或者不放的数组
   cout<<"请输入每个物品的重量和价值:(中间用空格隔开)"<<endl;
   for(i=1;i<n+1;i++)
      cin>>w[i]>>v[i];
   
   int **m = new int *[n+1];   //动态建立二维数组分配空间
   for(i=0;i<n+1;i++)
   m[i] = new int [c+1];
   Knapsack(v,w,c,n,m);
   Traceback(m,w,x,n,c);
   cout<<"能装的最大价值是:"<<m[1][c]<<endl;
   cout<<"可以装入的物品编号,物品重量和对应的价值是:"<<endl;
   for(i=1;i<n+1;i++)
   if(x[i]==1)cout<<"第"<<i<<"个物品:"<<w[i]<<"<<v[i]<<endl;
   return0;   
}
//cout<<"hello,bug!"<<endl;

template<classType>
voidKnapsack(Type *v,int *w,int c,int n,int **m)
{
   int i,j;
   int jMax = MAX(w[n]-1,c);
   if(jMax>c)jMax = c;
   for(j=0;j<=jMax;j++)m[n][j] = 0;
   
   for(j=w[n];j<=c;j++)m[n][j] = v[n];
   //上面是对于m[n][j]的处理
   
   for(i=n-1;i>1;i--)
   {
       jMax= MAX(w[i]-1,c);
      if(jMax>c)jMax=c;  
      for(j = 0;j<=jMax;j++) m[i][j] = m[i+1][j];  //不能选择i的情况
      for(j=w[i];j<=c;j++)m[i][j] = MAX(m[i+1][j],m[i+1][j-w[i]]+v[i]);
      //m[i+1][j]代表能放入第i个数,但是不放入。
      //m[i+1][j-w[i]]+v[i]代表放入了第i个数,至于前面能放入的数就看m[i+1][j-w[i]]的值了
      
      m[1][c] = m[2][c];  
      if(c>=w[1])m[1][c] = MAX(m[2][c],m[2][c-w[1]]+v[1]);   //因为第1这一行是属于没用到的子问题,所以就分开处理
   }
}


template<classType>
voidTraceback(Type **m ,int *w,int *x,intn,int c)
{
   int i;
   for(i=1;i<n;i++)
   if(m[i][c] == m[i+1][c])x[i] = 0;   //物品没装
   else
   {
       x[i] = 1;c -= w[i];   //物品装了,减去这一段的重量
   }
   x[n] = (m[n][c])?1:0;     //如果m[n][c]为0的话,显然没放,不然就放了
}





0-1背包问题

原创粉丝点击