0-1 背包问题

来源:互联网 发布:dota2深渊密室知乎 编辑:程序博客网 时间:2024/05/21 11:16

咋先不说解决这种背包问题的方法,个人感觉应该先把产生问题的背景描述出来会比较好一点,如下:

1,问题描述

有N件物品和一个容量为V的背包。第i件物品的重量是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。,

2,具体实例:

现有一个容量为10kg的背包,还有5件不同质量和价格的物品,现在让你,把这些物品有选择性的放到背包里面,使得背包的价值最大且背包里物品的重量不能超过背包的最大容量。5件物品的重量分别为{A,B,C,D,E}与之对应的质量分别为{2,2,6,5,4}对应的价格为{6,3,5,4,6}。

问题特点:

每种物品仅有一件,可以选择放或不放。

解决问题思路:

我把背包容量从0列举到10,因为背包容量太大,所以求解很复杂。故此,我就分别求,看背包容量分别为0,1,2,3....10时,怎样放物品能使不同容量的背包价值最大。放物品的时候也是一个一个的放,至于是否放得进去,看如下例子:

(想了好久也不知道该怎么叙述,这里我就直接举例说明,看我基友能不能看懂,要是他能看懂的话我,相信你们就能看懂)

现在,我准备把物品A往背包里面放,容易看出,背包容量为0和1时,物品A是放不进去的,所以价值为0,当背包容量为2,3,4....10时可以放进去,且与之对应的价值都是6,这应该好理解吧,就是说,背包容量为2时,把A放进去,背包满了,价值为6,容量为3时,A放进去时,背包没满,但价值还是6,以此类推后面的容量对应的价值都是6。如图,

 

现在,在以上操作的基础上(物品A放入相应容量的背包中使得背包的价值最大)再把物品B放入相应容量的背包中,由于B的重量为也是2,所以背包容量为0和1时不能放B,所以对应的价值,应该是上一次(放入A)时的最大价值,结果还是0,然后容量为2时,做出判断,如果放入B,背包满了不能放A了,价值是3,如果不放B,那么价值就是A的价值6,很显然6大于3   所以这儿应该放A,然后一直往后放,容量为4时,可以把A和B都放进去,价值为9,以后容量为5,6,7,8,9,10价值都是9  如下图


好了,现在可以把C也放进去了如图


最后把D和E也放进去


至此,所有的物品都已有选择性的放进去了,但是你还不知道到底哪个是真的放进去了,哪个没放进

不过,最大值可以看出来,就是15

找出最优解:

判断放进去的物品:

我们从最大值开始判断,背包容量为10时,(有选择性的放或不放E后)价值最大为15,(有选择性的放D或不放D后)价值最大为14,这就说明了一个问题,要是没有放E,那么最大价值应该是14,所以确定E肯定放进去了。

然后判断D是否放入了背包,判断D时,先把E的重量减掉(因为E已经确定放进去了,所以接下来判断的就是剩余背包容量中是否放入了D),10-4=6,看背包容量为6时的情况,有图可以看出,对D没有进行任何操作时的价值为9(看C行,容量为6的那个价值),然后对D进行 ”有选择性的放或不放“ 的操作后,价值还是9,那说明D肯定没放,

对C判断,同样的,先算出剩余背包容量,由于已近确定放E,没放D,所以剩余背包容量为10-4=6,有图可知,对C没有进行任何操作时的价值为9(看B行,容量为6的那个价值),然后对C进行 ”有选择性的放或不放“ 的操作后,价值还是9,那说明C肯定没放,

对B判断,以上可知,C,D都没放,放入了E,背包剩余容量还是6,对B没有进行任何操作时的价值为6(看A行,容量为6的那个价值),然后对B进行 ”有选择性的放或不放“ 的操作后,价值是9,那说明C肯定放进去了,

对A判断,以上可知剩余背包容量为10-4-2=4,对A没有进行任何操作时的价值为0(啥都不操作的时候),然后对A进行 ”有选择性的放或不放“ 的操作后,价值是6,那说明A肯定放进去了,

至此我们可以得出我们的最优解背包里放进去的物品分别是A,B,E,还可以知道,背包容量为2+2+4=8时,背包的价值就已经达到最大了。

下面附上代码

#include<stdio.h>  #include<stdlib.h>  #include<iostream>  using namespace std;  const int c = 10;             //背包的容量  const int w[] = {0,2,2,6,5,4};//物品的重量,其中0号位置不使用 。   const int v[] = {0,6,3,5,4,6};//物品对应的待加,0号位置置为空。  const int n = sizeof(w)/sizeof(w[0]) - 1 ; //n为物品的个数   int x[n+1];  void package0_1(int m[][11],const int w[],const int v[],const int n)//n代表物品的个数  用于进行“有选择性放或不放物品” 的操作{      //采用从底到顶的顺序来设置m[i][j]的值      //首先放w[n]  int i,j;    memset(m,0,sizeof(m));    for(i = 1; i <= n; i++)  {        for( j = 0; j <= c; j++)  {                      m[i][j] = m[i-1][j];//如果j < w[i]则,当前位置就不能放置,它等于上一个位置的值。                                              //否则,就比较到底是放置之后的值大,还是不放置的值大,选择其中较大者。    if(j>=w[i])   {                m[i][j] = m[i-1][j] > m[i-1][j-w[i]] + v[i]?                   m[i-1][j] : m[i-1][j-w[i]] + v[i];    }}}}  void answer(int m[][11],const int n)  //选出最优解,判断那个放入了背包,那个没有放,{      int j = c;      int i;      for(i = n; i>=1; i--)          if(m[i][j] == m[i-1][j])x[i] = 0;          else                {               x[i] = 1;              j = j - w[i];          }  }  int main()  {   int m[6][11]; memset(m,0,sizeof(m)); package0_1(m,w,v,n);   for(int i = 0; i <= 5; i++)   {       for(int j = 0; j <= 10; j++)  if(i==0)printf("%2d ",j); else     printf("%2d ",m[i][j]);      printf("\n");    }    answer(m,n);   printf("The best answer is:\n");  for( i = 1; i <= 5; i++)   printf("%d ",x[i]); system("pause");   return 0;  }  

  1.  m[i][j] = m[i-1][j] > m[i-1][j-w[i]] + v[i]?     
  2.                 m[i-1][j] : m[i-1][j-w[i]] + v[i];   
深度分析上述表达式

对于该式子的理解,首先得清楚一点就是每个物品只有两种状态  放或不放  例如那物品A和B来说,相互组合之后有三种情况

(1)放A不放B   (2)放B不放A   (3)即放A又放B

其中怎么判断到底选择哪种放发 主要就看上面这个公式,看左面,其中m [i-1] [j]表示的就是放A不放B时的背包的价值  m[i-1][j-w[i]]+v[i]这个式子有两层意思:只放B    A与B都放,具体的实现就要看是否满足对应的条件了,这里面,给我的感觉是,它直接假设把第i(这里是物品B)个物品放进去,下面我给你分析一下:


放B后(第i个物品),那么背包剩余的质量就是j-w[i],然后看前i-1(在这里只有物品A)个物品在剩余的背包质量中的价值是多少,即m[i-1][j-w[i]]。最后加上物品第i个(这里是物品B)物品的价值v[i],最终的公式是m[i-1][j-w[i]]+v[i]

j=2,3时

具体的例子是,现在开始放B,即判断m[2][j](j的范围是0到10)的值,我们一个一个的判断,因为B的质量为2,所以只有背包的质量等于2 时才开始进行方与不放的操作,那么现在使m[2][2]等于m[1][2]还是m[1][0]+v[1]   这里面m[1][2]的意思就是放A不放B ,那么价值就是A的价值=6,然后看m[1][0]+v[1]这里我前面说了,直接假设把B放进去,那么背包剩余质量为j-w[i]=0,由此可知前i-1个物品(在这里只有A)是放不进去的,那么价值为0,然后加上第i个物品的价值v[i]=3,所以最终的价值m[i-1][j-w[i]]+v[i]=3其中表达的意思是放B不放A,最终判断m[1][2]=6大于m[1][0]+v[1]=3所以m[2][2]=6,后面的j=3,判断方法于此一样

j=4,5,6,7,8,9,10时

这里,再说一下j=4时的情况,现在使m[2][4]等于m[1][4]还是m[1][2]+v[4]   这里面m[1][4]的意思就是放A不放B ,那么价值就是A的价值=6,然后看m[1][2]+v[4]这里我前面说了,直接假设把B放进去,那么背包剩余质量为j-w[i]=2,由此可知前i-1个物品(在这里只有A)是可以放进去的,那么价值为6,然后加上第i个物品的价值v[4]=3,所以最终的价值m[i-1][j-w[i]]+v[i]=9其中表达的意思是A和B都放进去,最终判断m[1][4]=6小于m[1][2]+v[4]=9所以m[2][4]=9.后面的j=5,6,7,8,9,10 的判断方法于此一样




0 0
原创粉丝点击