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; }
- 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];
对于该式子的理解,首先得清楚一点就是每个物品只有两种状态 放或不放 例如那物品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-1背包、完全背包、多重背包)详解
- 背包问题和0-1背包问题
- 背包问题和0-1背包问题
- 背包问题系列--"0-1背包问题"
- 背包笔记-含0/1背包问题、完全背包问题、多重背包问题、二维背包问题、分组背包问题
- 【背包问题】背包问题之0-1背包、完全背包、多重背包
- 0-1背包问题
- 0/1背包问题
- 0,1背包问题
- 0-1背包问题
- 0/1背包问题
- 0-1背包问题
- // 0-1背包问题
- 0/1背包问题
- 0-1背包问题
- 0-1背包问题
- 0-1背包问题
- 0/1背包问题
- Leetcode: Longest Palindromic Substring
- 单例模式的七种写法
- 无状态类在并发环境中绝对安全吗?
- Fitnesse使用系列四
- Max Points on a Line
- 0-1 背包问题
- 安卓虚拟机创建注意的问题
- Linux网络设置(第二版) --互联网寻址过程
- Html-DW(2)div & css 简单的三栏页面
- android 获取root权限
- mysql 总结
- unix时间 mysql数据库 时间段查询
- 大型网站架构演变和知识体系
- easyui与jqueryui哪个好用