背包01问题初理解

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。

具体理解可以直接通过下面的例子来理解背包问题:
背包问题的一个例子
背包问题的一个例子:应该选择哪些盒子,才能使价格尽可能地大,而保持重量小于或等于15 kg?

我们有n种物品,物品j的重量为wj,价格为pj。
我们假定所有物品的重量和价格都是非负的。背包所能承受的最大重量为W。
如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题。

01背包问题解法:
假定w1, …, wn和W都是正整数。我们将在总重量不超过Y的前提下,前j种物品的总价格所能达到的最高值定义为A(j, Y)。
A(j, Y)的递推关系为:
A(0, Y) = 0
A(j, 0) = 0
如果wj > Y, A(j, Y) = A(j - 1, Y)
如果wj ≤ Y, A(j, Y) = max { A(j - 1, Y), pj + A(j - 1, Y - wj)}

通过计算A(n, W)即得到最终结果。为提高算法性能,我们把先前计算的结果存入表中。因此算法需要的时间和空间都为O(nW),通过对算法的改进,空间的消耗可以降至O(W)。

下面我们通过解上面的例子来理解01背包问题的解法:
题目描述:
有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
我们先来看一张表:
这里写图片描述

我们只要弄清楚表格中A(n)怎么计算的,我们就知道了01背包算法的思想了。
首先,我们需要知道怎么去看这个表格。在表格上也做了简要的说明。这里我再添加一点说明。横坐标,表示背包最多承载多少重量,例如,在最后一列,w=10时即为我们本题所要求解的价值总和。我们可以看到,15即我们我们想求的最终答案。

那么,这个表格是怎么计算出来的呢?其实就是套公式就Ok了。
为了描述方便,将第一第一列成为a0(即横坐标上为0,纵坐标上为a,值为0的那个点)。例如a3=6。
我们套用前面的递推公式:
A(0, Y) = 0
A(j, 0) = 0
如果wj > Y, A(j, Y) = A(j - 1, Y)
如果wj ≤ Y, A(j, Y) = max { A(j - 1, Y), pj + A(j - 1, Y - wj)}

  1. 首先,计算e0的值,明显A(j, 0) = 0,所以为0;同理,在0列,全为0。这也横好理解,在包总共可以装0的重量下,所放物品价值总值一定为0;
  2. 然后计算e1,因为包总共可以装重量为1的物体,然e重量为4,所以一定装不下,所以包可以存放物品总价值为0,同理可得e1~e3全为0;
  3. 然后,我们开始考虑计算b2的值。因为包总共可以装2质量的物体,而b刚好重量为2,价值为3,所以我们不难得出此时A=3。同理可得a2=6;
  4. 接下来,是整个表最重要的部分。我们通过计算b4和a6来理解背包的过程。接下来我们就需要套用公式:A(j, Y) = max { A(j - 1, Y), pj + A(j - 1, Y - wj)}。首先,我们计算b4的值,b4的值是在两个值中取一个最大的值,我们一个一个来算,A(j-1,Y)这个值即在b3=A(j,Y)的下面一个格子,即c4=A(j-1,Y),在计算b4之前,我们已经用前三步将c4=6计算出来了,所以我们这里很容易就得出A(j-1,Y)=c4=6。接下来计算另外一个值pj + A(j - 1, Y - wj),在计算之前,有必要弄清楚未知量的含义,pj表示当前物品的价值,wj表示当前物品的重量。那么在计算b3物体时,pj + A(j - 1, Y - wj)应该等于 3+A(j-1,4-3)。其中的j-1表示b3的下一行。即A(j-1,1)表示c1。查表可以c1=0,即最终为3+0=3;两个值我们都计算出来了,一个等于6,一个等于3,所以这里我们应该取最大值6。同理,计算a6的值,应该为a6=max{c6,6+A(j-1,6-2)}=max{6,6+c4}=max{6,12}=12

PS. 其中表中还有一处错误,b9=max{c9,3+c7}=max{10,3+6}=10,而表中误写成9。不想再画图了,所以就没有修正了。

最后,我们来理解以下,最后一步为什么是这个样子的。可以理解的是A(j-1,Y)表示我有一个承重为多少的包,当只有abcd(不包括当前物体)四件可选时,这个包能装入的最大价值。
pj + A(j - 1, Y - wj)表示的是在我包括当前物体物体的情况下,这是背包能装纳入的最大价值为多少。
如此理解,应该还算是比较清晰把。

最后,附上C++实现代码:

#include<iostream>

using namespace std;

int main(){
    int w[]={2,2,6,5,4};//物品的重量 
    int p[]={6,3,5,4,6};// 物品的价值 
    int n=5,Y=10;       //n表示有5个物体,Y表示包可以装的重量为10 
    int A[11][11]={0};        //辅助数据 
    for(int j=1;j<Y+1;j++){
        for(int i=1;i<n+1;i++){
            if(w[i-1] > j )
                A[i][j] = A[i-1][j];
            else{
                A[i][j] = max(A[i-1][j],p[i-1]+A[i-1][j-w[i-1]]);
            }
        }
    }
    cout<<"背包最多可以装下"<<A[n][Y]<<" 价值的物品!";   
    return 0;
} 

运行结果就不截图了,很简单。自己可以动手试一试~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值