0-1背包问题

问题描述:
  给了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>
using namespace std;
int MAX( int a , int b)
{
    return a >b ? a:b;
}
template < class Type >
void Knapsack( Type * v , int * w , int c , int n , int ** m);

template < class Type >
void Traceback( Type ** m , int * w , int * x , int n , int c);

int main()
{
    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;
    return 0;    
}
//cout<<"hello,bug!"<<endl;

template < class Type >
void Knapsack( 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 < class Type >
void Traceback( Type ** m , int * w , int * x , int n , 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背包问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值