算法导论--动态规划(0-1背包问题)

背包问题

小偷发现了n个商品,第i个商品重量为 wi ,价值为 vi 。小偷希望尽量拿走价值高的商品,但是他的背包只能容纳W重的商品。求如何取舍这些商品?
由于对一个商品,要么被拿走要么不被拿走,所以被称为0-1背包问题。
我们如果采取枚举法进行比较,将会有 2n 个情况,算法复杂度与n呈指数关系。
下面分析背包问题的性质:

动态规划

最优子结构

xi =1,表示第i个商品被拿走, xi =0,表示第i个商品不被拿走。
则问题变为求 V=maxni=1xivi 约束条件为 ni=1xiwiW ,求最大值的 x1,x2,x3..xn 解;
对于第k个商品,决定是否装包,需要进行比较,如果拿装包即 xk=1 ,求子问题 V=maxni=1xivi(ik) ,约束条件为 ni=1xiwi(xk)Wwk 。如果不装包, xk=0 V′′=maxni=1xivi(ik) ,约束条件不变 ni=1xiwi(xk)W 。比较 VV′′ 大小。

自底向上求解方案

算法复杂度为O(nW)
令c[i][j]表示第1个商品到第i个商品中,背包容量为j的情况下,可获得的最大价值;

决定是否选择商品i的方案,比较选与不选的获得的价值
c[i][j] = max(c[i-1][j] ,v[i]+c[i-1][j-w[i]])
例:

int w[]={0,3,6,3,8,6};//商品重量 第一数值为0,为了方便编程
int v[]={0,4,6,6,12,10};//商品价值 第一数值为0,为了方便编程
int W = 10; //背包容量
int c[6][11]={0};//c[i][j]表示在商品1到i中,背包容量为j时,最大价值

采用自底向上求解方案,先填写第一行c[1][j],此时只有商品1可选,当 j<w[1] 时,背包容量小于商品1的大小,所以c[1][j] =0,当 jw[1] 时,背包内价值即为商品1的价值c[1][j] = v[1]=4;
这里写图片描述
填写第二行:c[2][j],此时可选商品为1和2 。当 j<w[2] ,商品2一定装不了,但可能装下商品1,所以即c[2][j] = c[1][j],当 jw[2] ,此时可以装下商品2,如当j=6时,如果选择商品2,那么此时背包容量为j-w[2]=0,留给商品1用,而c[1][0]=0,所以背包价值为c[2][6]=v[2]+c[1][0];如果不选商品2,则c[2][6]=c[1][6]=4,比较大小得到,应该把商品2装包。即c[2][6]=v[2]+c[1][0]=6+0=6;
又如:当j=10时,如果选择商品2,则背包容量还剩j-w[2]=4;而c[1][4]=4,此时背包总价值为c[2][10]=v[2]+c[1][4]=6+4=10;如果不选商品2,c[2][10] =c[1][10]=4;选取最大值即c[2][10]=10;
这里写图片描述
按照上述方式自底向上填写表格:
这里写图片描述

构造最优解

按照上面描述:如果c[i][j] = c[i-1][j],表明商品i没有被选择;否则就被选择
从表格的右下端开始,即c[5][10],回溯。
c[5][10]c[4][10] 则商品5被选择,而此时背包容量j-w[5]=4;继续向上回溯,比较c[4][4]=c[3][4],表明商品4不选。回溯到第一个商品时,如果 c[1][j]0 ,表明被装包;

完整代码

/************************************************************************
CSDN 勿在浮沙筑高台 
http://blog.csdn.net/luoshixian099
算法导论--动态规划(0-1背包问题)
2015年6月19日                    
************************************************************************/
#include <iostream>
using namespace std;
#define max(a,b) (((a) > (b)) ? (a) : (b))
int w[]={0,3,6,3,8,6};//商品重量
int v[]={0,4,6,6,12,10};//商品价值
int W = 10; //背包容量
int c[6][11]={0};//c[i][j]表示在商品1到i中,背包容量为j时,最大价值
void Package0_1(int w[],int v[],int W,int n,int c[][11])//
{

    for(int i=1;i<=n;i++)          //逐行填表c[i][j]
    {
        for (int j=1;j<=W;j++)
        {
            if ( i == 1)       //填写第1行时,不参考其他行
            {
                if (j < w[i])
                    c[i][j]=0;
                else
                    c[i][j] = v[i];
            }

            else
            {
                if ( j < w[i])  //背包容量小于商品i的重量,商品i一定不选
                {
                    c[i][j] = c[i-1][j];
                }
                else
                {
                    c[i][j] = max(c[i-1][j],v[i]+c[i-1][j-w[i]]);//比较选与不选商品i的背包总价值大小
                }
            }
        }
    }

    for(int m =1;m<6;m++)
    {
        for (int n=0;n<11;n++)
        {
            cout<<c[m][n]<<"  ";
        }
        cout<<endl;
    }

}
void Print_Package0_1(int c[][11])  //构造解
{
    int i=5;
    int j=10;
    cout<<"总价值为"<<c[i][j]<<endl;
    while(i!=1)
    {
        if ( c[i][j] == c[i-1][j] )
        {
            cout<<"商品"<<i<<"不选"<<endl;
        }
        else
        {
            cout<<"商品"<<i<<"选"<<endl;
            j = j - w[i];
        }
        i--;
    }

    if ( c[i][j] == 0)   //
    {
        cout<<"商品"<<i<<"不选"<<endl;
    }
    else
    {
        cout<<"商品"<<i<<"选"<<endl;
    }

}
int main()
{

   Package0_1(w,v,W,5,c);
   Print_Package0_1(c);
   return 0;
}

这里写图片描述

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值