0-1背包问题的多种解法

合集下载

0_1背包问题的多种解法

0_1背包问题的多种解法

一、 问题描述0/1背包问题:现有n 种物品,对1<=i<=n ,已知第i 种物品的重量为正整数W i ,价值为正整数V i ,背包能承受的最大载重量为正整数W ,现要求找出这n 种物品的一个子集,使得子集中物品的总重量不超过W 且总价值尽量大。

(注意:这里对每种物品或者全取或者一点都不取,不允许只取一部分)二、 算法分析根据问题描述,可以将其转化为如下的约束条件和目标函数:)2(max )1()1}(1,0{11∑∑==⎪⎩⎪⎨⎧≤≤∈≤ni i i ini i i x v n i x Wx w 于是,问题就归结为寻找一个满足约束条件(1),并使目标函数式(2)达到最大的解向量),......,,,(321n x x x x X =。

首先说明一下0-1背包问题拥有最优解。

假设),......,,,(321n x x x x 是所给的问题的一个最优解,则),......,,(32n x x x 是下面问题的一个最优解:∑∑==⎪⎩⎪⎨⎧≤≤∈-≤ni i i ini i i x v n i x x w W x w 2211max )2}(1,0{。

如果不是的话,设),......,,(32n y y y 是这个问题的一个最优解,则∑∑==>n i ni ii ii xv y v 22,且∑=≤+ni iiW yw x w 211。

因此,∑∑∑====+>+ni i i n i n i i i i i x v x v x v y v x v 1221111,这说明),........,,,(321n y y y x 是所给的0-1背包问题比),........,,,(321n x x x x 更优的解,从而与假设矛盾。

穷举法:用穷举法解决0-1背包问题,需要考虑给定n 个物品集合的所有子集,找出所有可能的子集(总重量不超过背包重量的子集),计算每个子集的总重量,然后在他们中找到价值最大的子集。

浅谈0-1背包问题的常用算法

浅谈0-1背包问题的常用算法
消 费 电子 Байду номын сангаас
2 0 1 3年 1 0月下 C o n s u me r E l e c t r o n i c s Ma g a z i n e 技 术 交 流
浅谈 0 - 1 背包问题的常用算法
汤赫 男
( 吉林工商学院信息工程学院,长春 1 3 0 0 6 2) 摘 要 :0 -1 背 包问题是典型的 NP ~完全问题 ,无论从 理论 上还是 实践上都有一定的研究意义。本文综述 了几 种0 — 1背包问题的 常用算法 ,分析算法的优劣 ,预 测 0 - 1背包问题的发展方向。 关键 词 :0 — 1背包问题 ;动 态规划法 ;贪心法 ;分支界限法


∑w ,
l {
㈠ { “ } m a x ∑
{ j
二 、常用 的 0 - 1 背 包问题算法 ( 一) 蛮力法。 蛮 力法又称穷举法或枚举法,是一种简单、 直接、有效的方法,是初学者入 门的方法 。蛮力法要求遍历所 有可能情 况一次且仅一次 ,筛选 出符合要求 的解。应用蛮力法 求解 0 - 1 背包 问题, 需要考虑给定的 n 个物品集合的所有子集, 找出所有总重量不超过背包容量的子集 ,计算每个可能子集的 总价值,然后找 出价值最大的子集 。对于一个具有 n个元素的 集合 ,其子集数量是 2 “,所 以,不论生成子集 的算法效率有 多高 ,蛮力法求解 0 - 1 背包 问题都会导致一个 Q ( 2 n )的算法 。 ( 二 )动 态规划法。动态规划 法是一种通用 的算 法设计 技术用来求解 多阶段决策最优 化问题。这类 问题都满 足最优 性原理,即原 问题 的最优 性包含着子 问题 的最优性 。 应用 动态规划法 求解 0 - 1 背包 问题 ,可 以将 0 — 1背包 问 题看 作一个 多阶段决策最 优化 问题 。n个物 品集合 的所 有子 集可 以看 作该 问题 的所有 可行解;这些可行解 都是满足约束 条件 的,可行解可能不止一个,通过 目标 函数找到最优解 。 动态 规划 法求解 0 - 1 背包 问题 的算法描述 : 设V ( n , C )表 示将 n个 物 品装入 容量 为 C的背 包获 得 的 最大价值 。 初 始 状 态 :V ( i , 0 ) = V ( 0 , j ) = 0 , 0≤ i ≤n , 0≤ j≤ C 则V ( i , j )表示 将前 i 个 物 品装入 容量 为 j的背 包获 得

0-1背包问题-贪心法和动态规划法求解1

0-1背包问题-贪心法和动态规划法求解1

实验四“0-1”背包问题一、实验目的与要求熟悉C/C++语言的集成开发环境:通过本实验加深对贪心算法、动态规划算法的理解。

二、实验内容:掌握贪心算法、动态规划算法的概念和基本思想,分析并掌握“0-1”背包问题的求解方法,并分析貝优缺点。

三、实验题1.“0-1”背包问题的贪心算法2.“0-1”背包问题的动态规划算法说明:背包实例采用教材P132习题六的6-1中的描述。

要求每种的算法都给出最大收益和最优解。

设有背包问题实例n=7, M=15,, (w0,wl,…w6)= (2, 3, 5, 7,1,4,1),物品装入背包的收益为:(p0, pl,。

,p6) = (10,5,15,7,6,18,3)。

求这一实例的最优解和最大收益。

四、实验步骤理解算法思想和问题要求;编程实现题目要求:上机输入和调试自己所编的程序:验证分析实验结果;整理岀实验报告。

五、实验程序//贪心法求解#i nclude 〈iostream> #include ,/iomanip // using namespace std;//按照单位物品收益排序,传入参数单位物品收益,物品收益和物品重量的 数组,运用冒泡排序void AvgBenefitsSort (float *arry_avgp, float *arry_p, float *arry_w ); /7获取最优解方法,传入参数为物品收益数组,物品重量数组,最后装载物 品最优解的数组和还可以装载物品的重量float GetBestBenifit (float *arry_p,float *arry_w, float*arry_x, float u);int main() {float w[7] = {2, 3, 5, 7, 1, 4, 1}; float p[7] = {10, 5, 15, 7, 6, 18, 3}; float avgp[7]={0);float x[7]二{0}; const float M=15; float ben=0; AvgBenefitsSort(avgp, p, w); ben=GetBestBenif it (p, w, x, M);cout«endK<ben«endl; /,/输 L L I M 后的收益system ("pause"); return 0;}//按照单位物品收益排序,传入参数单位物品收益,物品收益和物品重量的数组,运用冒泡排序void AvgBenefitsSort (float *arry_avgp, float *arry_p, float *arry_w ) {//求岀物品的单位收益for (int i=0;i<7;i++){arry_avgp[i]=arry_p[i]/arry_w[i];} cout«endl;//把求出的单位收益排序,冒泡排序法int exchange 二7;int bound=0;float temp=0; //物品重量数组//物品收益数组//单位毒品的收益数组//最后装载物品的最优解数组 //背包所能的载重//最后的收益while(exchange)bound=exchange; exchange二0;for (int i=0;i<bound;i++){if (arry_avgp[i]<arry_avgp[i+1]) {〃交换单位收益数组temp=arry_avgp[i];arry_avgp[i]=arry_avgp[i+1]; arry_avgp[i+1]=temp;//交艇收益数组temp=arry_p[i];arry_p[i]=arry_p[i+1]; arry_p[i+l]=temp;//交换重量数组temp=arry_w[i]; arry_w[i]=arry_w[i+1];arry_w[i+1]二t emp;exchange=i;}}}}//获取最优解方法,传入参数为物品收益数组,物品重量数组,最后装载物品最优解的数组和还可以装载物品的重量float GetBestBenifit (float *arry_p, float *arry_w, float *arry_x, float u){int i二0; ,7循坏变量ifloat benifit=0; //最后收益while (i<7){if(u~arry_w[i]>0){arry.X[i]=a rry_w[i]; //把半前物品重量缴入最优解数组benif it+=arry_p[i]; //收益增加前物品收益u-=arry_w[i]: //背包还能载逼量减去前物品巫cout«arry_x[i]//输出最优解} i++;}return benifit; //返回最后收益}//动态规划法求解nclude〈>#include<>#define n 6void DKNAP(int p[], int w[], int M, const int m): void main(){int p[n+l], w[n+l];int M, i, j;int m^l;for(i=l;i<=n;i++){m=m*2;printf C\nin put the weight and the p:〃);scanf (,z%d %d", &w[i], &p[i]);)printf (,z%d/z, m);printf (,z\n in put the max weight H:〃);scanf("%d", &M);DKNAP (p, w, M, m);}void DKNAP(int p[], int w[], int M, const int m){int p2[m], w2[m], pp, ww, px;int F[n+1], pk, q, k, 1, h, u, i, j, next, max, s[n+l];F[0]二1;p2[l]=w2[l]=0;l=h=l;F[l]二next二2;for(i=l;i<n;i 卄){k二1;max=0;u 二1;for (q=l;q<=h;q++)if ((w2 [q]+w[i] <=M) &&max<=w2 [q] +w [ i ]){u二q;max=w2[q] +w[i];}for(j=l;j<=u;j++){PP二p2[j]+p[i];ww二w2[j]+w[i];while(k<=h&&w2[k]〈ww) {p2[next]=p2[k];w2[next]=w2[k];next++;k++;}辻(k<=h&&w2[k]二二ww){if(pp<=p2[k])pp二p2 [k];k++;}else if(pp>p2[next-1]){p2[next]=pp;w2[next]=ww;next++;}while (k<=h&&p2[k]<=p2[next-1])k++;}while(k<=h){p2[next]=p2[k];w2[next]二w2[k];next二next+1; k++;}1二h+1;h二next-1;F[i+1]二next;}for(i=l;i<next;i++)printf C%2d%2d ", p2[i], w2[i]); for (i=n;i>0;i一一)next二F[i];next一一;pp=pk=p2[next];ww=w2[next];while (ww+w[i] >M&&next>F[iT])next=next~l; pp=p2[next]; ww二w2[next];}if(ww+w[i]<=M&&next>F[i -1]) px=pp+p[i];if(px>pk&&ww+w[i]<=M){s[i]二1; M=M-w[i]; printf (z,M=%d ",M);}else s[i]二0;)for (i=l;i<=n;i++)printf("%2d",s[i]);)六、实验结果1.贪心法截图:七、实验分析。

蛮力法、动态规划法、回溯法和分支限界法求解01背包问题【精选】

蛮力法、动态规划法、回溯法和分支限界法求解01背包问题【精选】

一、实验内容:分别用蛮力法、动态规划法、回溯法和分支限界法求解0/1背包问题。

注:0/1背包问题:给定种物品和一个容量为的背包,物品的重n C i 量是,其价值为,背包问题是如何使选择装入背包内的物品,使得装i w i v 入背包中的物品的总价值最大。

其中,每种物品只有全部装入背包或不装入背包两种选择。

二、所用算法的基本思想及复杂度分析:1.蛮力法求解0/1背包问题:1)基本思想:对于有n 种可选物品的0/1背包问题,其解空间由长度为n 的0-1向量组成,可用子集数表示。

在搜索解空间树时,深度优先遍历,搜索每一个结点,无论是否可能产生最优解,都遍历至叶子结点,记录每次得到的装入总价值,然后记录遍历过的最大价值。

2)代码:#include<iostream>#include<algorithm>using namespace std;#define N 100//最多可能物体数struct goods //物品结构体{int sign;//物品序号int w;//物品重量int p;//物品价值}a[N];bool m(goods a,goods b){return (a.p/a.w)>(b.p/b.w);}int max(int a,int b){return a<b?b:a;}int n,C,bestP=0,cp=0,cw=0;int X[N],cx[N];/*蛮力法求解0/1背包问题*/int Force(int i){if(i>n-1){if(bestP<cp&&cw+a[i].w<=C){for (int k=0;k<n;k++)X[k]=cx[k];//存储最优路径bestP=cp;}return bestP;}cw=cw+a[i].w;cp=cp+a[i].p;cx[i]=1;//装入背包Force(i+1);cw=cw-a[i].w;cp=cp-a[i].p;cx[i]=0;//不装入背包Force(i+1);return bestP;}int KnapSack1(int n,goods a[],int C,int x[]){Force(0);return bestP;}int main(){goods b[N];printf("物品种数n: ");scanf("%d",&n);//输入物品种数printf("背包容量C: ");scanf("%d",&C);//输入背包容量for (int i=0;i<n;i++)//输入物品i 的重量w 及其价值v {printf("物品%d 的重量w[%d]及其价值v[%d]:",i+1,i+1,i+1);scanf("%d%d",&a[i].w,&a[i].p);b[i]=a[i];}int sum1=KnapSack1(n,a,C,X);//调用蛮力法求0/1背包问题printf("蛮力法求解0/1背包问题:\nX=[ ");for(i=0;i<n;i++)cout<<X[i]<<" ";//输出所求X[n]矩阵printf("]装入总价值%d\n",sum1);bestP=0,cp=0,cw=0;//恢复初始化}3)复杂度分析:蛮力法求解0/1背包问题的时间复杂度为:。

0_1背包问题的多种解法

0_1背包问题的多种解法

、问题描述0/1背包问题:现有n种物品,对1<=i<=n,已知第i种物品的重量为正整数W,价值为正整数V,背包能承受的最大载重量为正整数V,现要求找出这n种物品的一个子集,使得子集中物品的总重量不超过W且总价值尽量大。

(注意:这里对每种物品或者全取或者一点都不取,不允许只取一部分)、算法分析根据问题描述,可以将其转化为如下的约束条件和目标函数:nw i x i Wi i ⑴X i {0,1(1 i n)nmax v i x (2)i 1于是,问题就归结为寻找一个满足约束条件( 1),并使目标函数式(2)达到最大的解向量首先说明一下0-1背包问题拥有最优解假设(X1, X2,X3,……,X n)是所给的问题的一个最优解,则 (X2,X3,……,X n)是下面问题的一个最优解:nWi 2X i {0,1}(2W1X1 maxi n) inv i X。

如果不是的话,设(y2> y3>....2..,y n)是这个问题的一个最优解,则n nV i y i V i X ii 2 i 2,且 W1X1n nW i y i W。

因此,V1X1 V i y ii 2 i 2n nV1X1V j X VX i,这说明i 2 i 1(X1,y2,y3, ....... , y n)是所给的0-1背包问题比(X1,X2,X3, ............ , X n)更优的解,从而与假设矛盾穷举法:用穷举法解决0-1背包问题,需要考虑给定n个物品集合的所有子集,找出所有可能的子集(总重量不超过背包重量的子集),计算每个子集的总重量,然后在他们中找到价值最大的子集。

由于精品(X1, X2,X3,……X n)。

程序过于简单,在这里就不再给出,用实例说明求解过程。

下面给出了4个物品和一个容量为10的背包,下图就是用穷举法求解0-1背包问题的过程。

(a)四个物品和一个容量为10的背包(b)用回溯法求解0-1背包问题的过程递归法:在利用递归法解决0-1背包问题时,我们可以先从第n个物品看起。

求解0—1背包问题算法综述

求解0—1背包问题算法综述

0-1背包问题是一种常见的动态规划问题,其目标是在给定背包容量和物品集合的情况下,选择某些物品放入背包,使得背包内物品的总价值最大。

以下是求解0-1背包问题的算法综述:
1. 定义变量和参数:
* 物品集合:包括每个物品的重量和价值。

* 背包容量:表示背包能够容纳的最大重量。

* dp数组:用于存储每个状态下的最大价值,dp[i][j]表示前i个物品、背包承重为j时的最大价值。

2. 初始化dp数组:
* 对于每个物品i和背包容量j,如果物品i能够装入背包,则令dp[i][j]为0;否则,令dp[i][j]为负无穷。

3. 递推计算dp数组:
* 对于每个物品i和背包容量j,如果物品i能够装入背包,则令dp[i][j]为当前物品的价值加上前i-1个物品、背包容量为j-w[i]时的最大价值,即dp[i][j] = dp[i-1][j-w[i]] + p[i];否则,
令dp[i][j]为前i-1个物品、背包容量为j时的最大价值,即dp[i][j] = dp[i-1][j]。

4. 返回dp数组的最后一个元素,即为所求的最大价值。

以上是求解0-1背包问题的算法综述,实际实现时可以根据具体情况进行优化,以提高算法的效率和性能。

用动态规划法求解0-1背包问题

用动态规划法求解0-1背包问题

0 — 1背包 问题 的解 决 方法 多 种 多样 ,常用 的算法 有贪 心算 法 、 回溯法 、 分 枝一限界法 等 。本文 采用 动态
规 划 原理 来 求 解 0 一 l背 包 问题 也不 失 为 一 种 简单 明 了、 清 晰 易懂 的方法 。 参考 文献 :
[ 1 ] 王 晓东. 计算机 算法设计与分析 [ M] . 北京: 电子 工业 出版社
w h i l e( m【 i Ⅱ c 】 = = m[ i 一 1 ] [ c ] ) i - - ; w h i l e( i > 0 ) { j = i 一 1 ; w h i l e( m『 j 1 [ c ] 一 m [ j 】 【 c ] != v i i - 1 ] & &- j > 0 )
[ i ] [ j 】 是 下 面两 个 量 的最 大值 : m[ i + 1 ] [ j ] 和 m【 i + 1 】 【 j — w [ i 】
] + V 嘲


f o r ( j = 0 ; j < = c ; j + + ) p r i n t f ( ” %3 d . t , m f i 1 【 j 】 ) ; p i f n f ( ” \ I 1 ” ) ;}
等于 v 『 n 1 ;
k n a p s a c k ( ) ;d i s p O ; p r i n t f ( ” 最 大价值= %d \ n ” , m 【 n ] [ c 】 ) ;
o f r ( i _ 0 ; i < = n ; i + + )
②当前的背包容量 J 大于等于物品重量 w [ i ] 时, m
2 0 07 .
i n t n , C , w [ M A X ] , v [ MA X ] , m [ MA x】 [ MA x 】 = { 0 } ; v o i d k n a p s a c k 0 {i n t i ;

背包问题

背包问题

(0-1)背包问题的解法小结1.动态规划法递推关系:– 考虑一个由前i 个物品(1≤i ≤n )定义的实例,物品的重量分别为w 1,…,w i ,价值分别为v 1,…,v i ,背包的承重量为j (1≤j ≤W )。

设V [I,j]为该实例的最优解的物品总价值– 分成两类子集:• 根据定义,在不包括第i 个物品的子集中,最优子集的价值是V [i -1,j ]• 在包括第i 个物品的子集中(因此,j -w ≥0),最优子集是由该物品和前i -1个物品中能够放进承重量为i -w j 的背包的最优子集组成。

这种最忧子集的总价值等于v i +V [i -1,j -w i ].0]0,[时,0 当0;][0,时,0初始条件:当],1[}],1[],,1[max{],[=≥=≥<≥⎩⎨⎧-+---=i V i j V j w j w j j i V v w j i V j i V j i V i i i i以记忆功能为基础的算法:用自顶向下的方式对给定的问题求解,另外维护一个类似自底向上动态规划算法使用的表格。

一开始的时候,用一种“null”符号创始化表中所有的单元,用来表明它们还没有被计算过。

然后,一旦需要计算一个新的值,该方法先检查表中相应的单元:如果该单元不是“null ”,它就简单地从表中取值;否则,就使用递归调用进行计算,然后把返回的结果记录在表中。

算法 MFKnapsack(I,j)//对背包问题实现记忆功能方法//输入:一个非负整数i 指出先考虑的物品数量,一个非负整数j 指出了背包的承重量 //输出:前i 个物品的最伏可行子集的价值//注意:我们把输入数组Weights[1..n],Values[1..n]和表格V[0..n,0..W]作为全局变量,除了行0和列0用0初始化以外,V 的所有单元都用-1做初始化。

if V[I,j]<01if j<Weights[i]value ←MFKnapsack(i-1,j)elsevalue ←max(MFKnapsack(i-1),j), Value[i]+MFKnapsack(i-1,j-eights[i]))V[I,j]←valuereturn V[I,j]2.贪心算法1) 背包问题基本步骤:首先计算每种物品单位重量的价值Vi/Wi ,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

问题描述0/1 背包问题 :现有 n 种物品,对 1<=i<=n ,已知第 i 种物品的重量为正整数 W i ,价值为正整数 V i , 背包能承受的最大载重量为正整数 W ,现要求找出这 n 种物品的一个子集,使得子集中物 品的总重量不超过 W 且总价值尽量大。

(注意:这里对每种物品或者全取或者一点都不取, 不允许只取一部分)算法分析根据问题描述,可以将其转化为如下的约束条件和目标函数:nw i x i W i 1 i i(1)x i { 0,1}( 1 i n)nmax v i x i (2) i1于是,问题就归结为寻找一个满足约束条件( 1 ),并使目标函数式( 2 )达到最大的 解向量 X (x 1, x 2 ,x 3, ........... , x n ) 。

首先说明一下 0-1 背包问题拥有最优解。

假设 (x 1,x 2,x 3, ........ ,x n ) 是所给的问题的一个最优解, 则(x 2,x 3, ............... ,x n )是下面问题的n n n个问 题 的 一 个 最 优解 , 则v i y iv i x i , 且 w 1x 1w i y i W 。

因此 ,i 2 i 2 i 2一个最优解:w i x i Wi2w 1x 1nmax v i x i 。

如果不是的话,设(y 2,y 3, , y n ) 是这x i {0,1}( 2 i n)i2n n nv1x1 v i y i v1x1 v i x i v i x i ,这说明(x1,y2,y3, ............. ,y n) 是所给的0-1 背包问i 2 i 2 i 1题比( x1 , x 2 , x3 , ... , x n ) 更优的解,从而与假设矛盾。

穷举法:用穷举法解决0-1 背包问题,需要考虑给定n 个物品集合的所有子集,找出所有可能的子集(总重量不超过背包重量的子集) ,计算每个子集的总重量,然后在他们中找到价值最大的子集。

由于程序过于简单,在这里就不再给出,用实例说明求解过程。

下面给出了4个物品和一个容量为10 的背包,下图就是用穷举法求解0-1 背包问题的过程。

a) 四个物品和一个容量为10 的背包序号子集总重量总价值序号子集总重量总价值1空集009{2,3}7522{1}74210{2,4}837 3{2}31211{3,4}965 4{3}44012{1,2,3}14不可行5{4}52513{1,2,4}15不可行6{1,2}105414{1,3,4}16不可行7{1,3}11不可行15{2,3,4}12不可行物品1W2=3V2=12物品2W3=4V3=40物品3W4=5V4=25物品4背包W1=7 V1=12(b )用回溯法求解0-1 背包问题的过程递归法:在利用递归法解决0-1 背包问题时,我们可以先从第n 个物品看起。

每次的递归调用都会判断两种情况:(1 ) 背包可以放下第n 个物品,则x[n]=1 ,并继续递归调用物品重量为W-w[n], 物品数目为n-1 的递归函数,并返回此递归函数值与v[n] 的和作为背包问题的最优解;(2) 背包放不下第n 个物品,则x[n]=0 ,并继续递归调用背包容量为W ,物品数目为n-1 的递归函数,并返回此递归函数值最为背包问题的最优解。

递归调用的终结条件是背包的容量为0 或物品的数量为0. 此时就得到了0-1 背包问题的最优解。

用递归法解0-1 背包问题可以归结为下函数:KnapSack(n 1,m) 没有选择物品nKnapSack(n, m)KnapSack(n 1,m w[n]) v[n] 选择了物品n第一个式子表示选择物品n 后得到价值KnapSack (n 1,m w[n]) v[n] 比不选择物品n 情况下得到的价值KnapSack(n 1,m) 小,所以最终还是不选择物品n; 第二个式子刚好相反,选择物品n 后的价值KnapSack(n 1,m w[n]) v[n] 不小于不选择物品n 情况下得到了价值KnapSack ( n1,m) ,所以最终选择物品n。

在递归调用的过程中可以顺便求出所选择的物品。

下面是标记物品被选情况的数组x[n] 求解的具体函数表示:0 KnapSack (n, m) KnapSack ( n 1,m)x[n]1 KnapSack (n, m) KnapSack ( n 1,m w[n]) v[n]在函数中,递归调用的主体函数为KnapSack ,m 表示背包的容量,n 表示物品的数量,x[n]表示是否选择了第n 个物品( 1—选,0—不选)。

每个物品的重量和价值信息分别存放在数组w[n] 和v[n] 中。

具体的代码见《递归法》文件夹。

贪心法:0-1 背包问题与背包问题类似,所不同的是在选择物品i(1 i n) 装入背包时,可以选择一部分,而不一定要全部装入背包。

这两类问题都具有最优子结构性质,相当相似。

但是背包问题可以用贪心法求解,而0-1 背包问题却不能用贪心法求解。

贪心法之所以得不到最优解,是由于物品不允许分割,因此,无法保证最终能将背包装满,部分闲置的背包容量使背包单位重量的价值降低了。

事实上,在考虑0-1 背包问题时,应比较选择物品和不选择物品所导致的方案,然后做出最优解。

由此导出了许多相互重叠的子问题,所以,0-1背包问题可以用动态规划法得到最优解。

在这里就不再用贪心法解0-1 背包问题了。

动态规划法分析:0-1 背包问题可以看作是寻找一个序列( x1, x2, x3 , ............... , x n ) ,对任一个变量x i 的判断是决定x i=1 还是x i=0. 在判断完x i 1之后,已经确定了( x1, x2, x3 , ................. , x i 1) ,在判断x i时,会有两种情况:(1) 背包容量不足以装入物品i,则x i =0 ,背包的价值不增加;(2) 背包的容量可以装下物品i,则x i=1 ,背包的价值增加v i。

这两种情况下背包的总价值的最大者应该是对x i 判断后的价值。

令C(i, j) 表示在前i(1 i n) 个物品中能够装入容量为j (1 j W )的背包的物品的总价值,则可以得到如下的动态规划函数:C(i,0) C(0, j) 0(1)C(i 1, j) j w iC(i, j ) i(2)max{C(i 1, j),C(i 1, j w i) v i} j w i式(1)说明:把前面i个物品装入容量为0 的背包和把0个物品装入容量为j 的背包,得到的价值均为0.式(2)第一个式子说明:如果第i 个物品的重量大于背包的容量,则装入第i 个物品得到的最大价值和装入第i-1 个物品得到的最大价值是相同的,即物品i 不能装入背包中;第二个式子说明:如果第i 个物品的重量小于背包的容量,则会有两种情况: ( 1 )如果把第i 个物品装入背包,则背包中物品的价值就等于把前i-1 个物品装入容量为j w i的背包中的价值加上第i个物品的价值v i ;(2 )如果第i 个物品没有装入背包,则背包中物品的价值就是等于把前i-1 个物品装入容量为j 的背包中所取得的价值。

显然,取二者中价值较大者作为把前i 个物品装入容量为j的背包中的最优解。

我们可以一步一步的解出我们所需要的解。

第一步,只装入第一个物品,确定在各种情况下背包能得到的最大价值;第二步,只装入前两个物品,确定在各种情况下的背包能够得到的最大价值;一次类推,到了第n 步就得到我们所需要的最优解了。

最后,C(n,W) 便是在容量为W 的背包中装入n 个物品时取得的最大价值。

为了确定装入背包的具体物品,从C(n,W)的值向前寻找,如果C(n,W)>C(n 1,W ) ,说明第n 个物品被装入了背包中,前n-1 个物品被装入容量为W w n的背包中;否则,第n 个物品没有装入背包中,前n-1 个物品被装入容量为W 的背包中。

依此类推,直到确定第一个物品是否被装入背0C(i, j) C(i 1, j)包为止。

由此,我们可以得到如下的函数:1, j j w i C(i, j) C(i 1,j)根据动态规划函数,用一个(n 1) (W 1)的二维数组C存放中间变量,C[i][ j]表示把前i个物品装入容量为j 的背包中获得的最大价值。

设物品的重量存放在数组w[n] 中,价值存放在数组v[n] 中,背包的容量为W ,数组C[n 1][W 1]存放迭代的结果,数组x[n] 存放装入背包的物品,动态规划解0-1 背包问题的源代码在文件夹《动态规划法》中。

回溯法分析:用回溯法解0_1 背包问题时,会用到状态空间树。

在搜索状态空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。

当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。

设r 是当前剩余物品价值总和;cp 是当前价值;bestp 是当前最优价值。

当cp+r ≤bestp 时,可剪去右子树。

计算右子树中解的上界可以用的方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。

由此得到的价值是右子树中解的上界,用此值来剪枝。

为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考察各物品即可。

在实现时,由MaxBoundary 函数计算当前结点处的上界。

它是类Knap 的私有成员。

Knap 的其他成员记录了解空间树种的节点信息,以减少函数参数的传递以及递归调用时所需要的栈空间。

在解空间树的当前扩展结点处,仅当要进入右子树时才计算上界函数MaxBoundary ,以判断是否可以将右子树减去。

进入左子树时不需要计算上界,因为其上界与父结点的上界相同。

在调用函数Knapsack 之前,需要先将各物品依其单位重量价值从达到小排序。

为此目的,我们定义了类Objiect 。

其中,运算符与通常的定义相反,其目的是为了方便调用已有的排序算法。

在通常情况下,排序算法将待排序元素从小到大排序。

在搜索状态空间树时,由函数Backtrack 控制。

在函数中是利用递归调用的方法实现了空间树的搜索。

具体的代码见《回溯法》文件夹。

限界分支法:在解0-1 背包问题的优先队列式界限分支法中,活结点优先队列中结点元素N 的优先级由该结点的上界函数MaxBoundary 计算出的值uprofit 给出。

该上界函数在0-1 背包问题的回溯法总已经说明过了。

子集树中以结点N 为根的子树中任一个结点的价值不超过N.profit 。

因此我们用一个最大堆来实现活结点优先队列。

堆中元素类型为HeapNode,其私有成员有uprofit,profit,weight,level, 和ptr 。

相关文档
最新文档