算法 0-1背包问题

合集下载

动态规划与回溯法解决0-1背包问题

动态规划与回溯法解决0-1背包问题

0-1背包动态规划解决问题一、问题描述:有n个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?二、总体思路:根据动态规划解题步骤(问题抽象化、建立模型、寻找约束条件、判断是否满足最优性原理、找大问题与小问题的递推关系式、填表、寻找解组成)找出01背包问题的最优解以及解组成,然后编写代码实现。

原理:动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。

但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。

过程:a) 把背包问题抽象化(X1,X2,…,Xn,其中 Xi 取0或1,表示第i 个物品选或不选),V i表示第i 个物品的价值,W i表示第i 个物品的体积(重量);b) 建立模型,即求max(V1X1+V2X2+…+VnXn);c) 约束条件,W1X1+W2X2+…+WnXn<capacity;d) 定义V(i,j):当前背包容量j,前i 个物品最佳组合对应的价值;e) 最优性原理是动态规划的基础,最优性原理是指“多阶段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略”。

判断该问题是否满足最优性原理,采用反证法证明:假设(X1,X2,…,Xn)是01背包问题的最优解,则有(X2,X3,…,Xn)是其子问题的最优解,假设(Y2,Y3,…,Yn)是上述问题的子问题最优解,则理应有(V2Y2+V3Y3+…+V n Yn)+V1X1 > (V2X2+V3X3+…+VnXn)+V1X1;而(V2X2+V3X3+…+VnXn)+V1X1=(V1X1+V2X2+…+VnXn),则有(V2Y2+V3Y3+…+VnYn)+V1X1 > (V1X1+V2X2+…+VnXn);该式子说明(X1,Y2,Y3,…,Yn)才是该01背包问题的最优解,这与最开始的假设(X1,X2,…,Xn)是01背包问题的最优解相矛盾,故01背包问题满足最优性原理;f) 寻找递推关系式,面对当前商品有两种可能性:第一,包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即V(i,j)=V(i-1,j);第二,还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i) }其中V(i-1,j)表示不装,V(i-1,j-w(i))+v(i) 表示装了第i个商品,背包容量减少w(i)但价值增加了v(i);由此可以得出递推关系式:1) j<w(i) V(i,j)=V(i-1,j)2) j>=w(i) V(i,j)=max{ V(i-1,j),V(i-1,j-w(i))+v(i) }number=4,capacity=7四、构造最优解:最优解的构造可根据C列的数据来构造最优解,构造时从第一个物品开始。

贪心算法-01背包问题

贪心算法-01背包问题

贪⼼算法-01背包问题1、问题描述:给定n种物品和⼀背包。

物品i的重量是wi,其价值为vi,背包的容量为C。

问:应如何选择装⼊背包的物品,使得装⼊背包中物品的总价值最⼤?形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找⼀n元向量(x1,x2,…,xn,), xi∈{0,1}, ∋ ∑ wi xi≤c,且∑ vi xi达最⼤.即⼀个特殊的整数规划问题。

2、最优性原理:设(y1,y2,…,yn)是 (3.4.1)的⼀个最优解.则(y2,…,yn)是下⾯相应⼦问题的⼀个最优解:证明:使⽤反证法。

若不然,设(z2,z3,…,zn)是上述⼦问题的⼀个最优解,⽽(y2,y3,…,yn)不是它的最优解。

显然有∑vizi > ∑viyi (i=2,…,n)且 w1y1+ ∑wizi<= c因此 v1y1+ ∑vizi (i=2,…,n) > ∑ viyi, (i=1,…,n)说明(y1,z2, z3,…,zn)是(3.4.1)0-1背包问题的⼀个更优解,导出(y1,y2,…,yn)不是背包问题的最优解,⽭盾。

3、递推关系:设所给0-1背包问题的⼦问题的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优值。

由0-1背包问题的最优⼦结构性质,可以建⽴计算m(i,j)的递归式:注:(3.4.3)式此时背包容量为j,可选择物品为i。

此时在对xi作出决策之后,问题处于两种状态之⼀:(1)背包剩余容量是j,没产⽣任何效益;(2)剩余容量j-wi,效益值增长了vi ;使⽤递归C++代码如下:#include<iostream>using namespace std;const int N=3;const int W=50;int weights[N+1]={0,10,20,30};int values[N+1]={0,60,100,120};int V[N+1][W+1]={0};int knapsack(int i,int j){int value;if(V[i][j]<0){if(j<weights[i]){value=knapsack(i-1,j);}else{value=max(knapsack(i-1,j),values[i]+knapsack(i-1,j-weights[i]));}V[i][j]=value;}return V[i][j];}int main(){int i,j;for(i=1;i<=N;i++)for(j=1;j<=W;j++)V[i][j]=-1;cout<<knapsack(3,50)<<endl;cout<<endl;}不使⽤递归的C++代码:简单⼀点的修改//3d10-1 动态规划背包问题#include <iostream>using namespace std;const int N = 4;void Knapsack(int v[],int w[],int c,int n,int m[][10]);void Traceback(int m[][10],int w[],int c,int n,int x[]);int main(){int c=8;int v[]={0,2,1,4,3},w[]={0,1,4,2,3};//下标从1开始int x[N+1];int m[10][10];cout<<"待装物品重量分别为:"<<endl;for(int i=1; i<=N; i++){cout<<w[i]<<" ";}cout<<endl;cout<<"待装物品价值分别为:"<<endl;for(int i=1; i<=N; i++){cout<<v[i]<<" ";}cout<<endl;Knapsack(v,w,c,N,m);cout<<"背包能装的最⼤价值为:"<<m[1][c]<<endl;Traceback(m,w,c,N,x);cout<<"背包装下的物品编号为:"<<endl;for(int i=1; i<=N; i++){if(x[i]==1){cout<<i<<" ";}}cout<<endl;return 0;}void Knapsack(int v[],int w[],int c,int n,int m[][10]){int jMax = min(w[n]-1,c);//背包剩余容量上限范围[0~w[n]-1] for(int j=0; j<=jMax;j++){m[n][j]=0;}for(int j=w[n]; j<=c; j++)//限制范围[w[n]~c]{m[n][j] = v[n];}for(int i=n-1; i>1; i--){jMax = min(w[i]-1,c);for(int j=0; j<=jMax; j++)//背包不同剩余容量j<=jMax<c{m[i][j] = m[i+1][j];//没产⽣任何效益}for(int j=w[i]; j<=c; j++) //背包不同剩余容量j-wi >c{m[i][j] = max(m[i+1][j],m[i+1][j-w[i]]+v[i]);//效益值增长vi }}m[1][c] = m[2][c];if(c>=w[1]){m[1][c] = max(m[1][c],m[2][c-w[1]]+v[1]);}}//x[]数组存储对应物品0-1向量,0不装⼊背包,1表⽰装⼊背包void Traceback(int m[][10],int w[],int c,int n,int x[]){for(int i=1; i<n; i++){if(m[i][c] == m[i+1][c]){x[i]=0;}else{x[i]=1;c-=w[i];}}x[n]=(m[n][c])?1:0;}运⾏结果:算法执⾏过程对m[][]填表及Traceback回溯过程如图所⽰:从m(i,j)的递归式容易看出,算法Knapsack需要O(nc)计算时间; Traceback需O(n)计算时间;算法总体需要O(nc)计算时间。

回溯法解决0-1背包问题

回溯法解决0-1背包问题

回溯法解决0-1背包问题问题描述: 有n件物品和⼀个容量为c的背包。

第i件物品的价值是v[i],重量是w[i]。

求解将哪些物品装⼊背包可使价值总和最⼤。

所谓01背包,表⽰每⼀个物品只有⼀个,要么装⼊,要么不装⼊。

回溯法: 01背包属于找最优解问题,⽤回溯法需要构造解的⼦集树。

在搜索状态空间树时,只要左⼦节点是可⼀个可⾏结点,搜索就进⼊其左⼦树。

对于右⼦树时,先计算上界函数,以判断是否将其减去,剪枝啦啦!上界函数bound():当前价值cw+剩余容量可容纳的最⼤价值<=当前最优价值bestp。

为了更好地计算和运⽤上界函数剪枝,选择先将物品按照其单位重量价值从⼤到⼩排序,此后就按照顺序考虑各个物品。

#include <stdio.h>#include <conio.h>int n;//物品数量double c;//背包容量double v[100];//各个物品的价值double w[100];//各个物品的重量double cw = 0.0;//当前背包重量double cp = 0.0;//当前背包中物品价值double bestp = 0.0;//当前最优价值double perp[100];//单位物品价值排序后int order[100];//物品编号int put[100];//设置是否装⼊//按单位价值排序void knapsack(){int i,j;int temporder = 0;double temp = 0.0;for(i=1;i<=n;i++)perp[i]=v[i]/w[i];for(i=1;i<=n-1;i++){for(j=i+1;j<=n;j++)if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]{temp = perp[i];perp[i]=perp[i];perp[j]=temp;temporder=order[i];order[i]=order[j];order[j]=temporder;temp = v[i];v[i]=v[j];v[j]=temp;temp=w[i];w[i]=w[j];w[j]=temp;}}}//回溯函数void backtrack(int i){double bound(int i);if(i>n){bestp = cp;return;}if(cw+w[i]<=c){cw+=w[i];cp+=v[i];put[i]=1;backtrack(i+1);cw-=w[i];cp-=v[i];}if(bound(i+1)>bestp)//符合条件搜索右⼦数backtrack(i+1);}//计算上界函数double bound(int i){double leftw= c-cw;double b = cp;while(i<=n&&w[i]<=leftw){leftw-=w[i];b+=v[i];i++;}if(i<=n)b+=v[i]/w[i]*leftw;return b;}int main(){int i;printf("请输⼊物品的数量和容量:");scanf("%d %lf",&n,&c);printf("请输⼊物品的重量和价值:");for(i=1;i<=n;i++){printf("第%d个物品的重量:",i);scanf("%lf",&w[i]);printf("价值是:");scanf("%lf",&v[i]);order[i]=i;}knapsack();backtrack(1);printf("最有价值为:%lf\n",bestp);printf("需要装⼊的物品编号是:");for(i=1;i<=n;i++){if(put[i]==1)printf("%d ",order[i]);}return 0;}时间复杂度分析: 上界函数bound()需要O(n)时间,在最坏的情况下有O(2^n)个右⼦结点需要计算上界,回溯算法backtrack需要的计算时间为O(n2^n)。

遗传算法求解0-1背包问题(JAVA)

遗传算法求解0-1背包问题(JAVA)

遗传算法求解0-1背包问题一、问题描述给定n种物品和容量为C的背包。

物品i的重量是wi,其价值为vi。

问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?二、知识表示1、状态表示(1)个体或染色体:问题的一个解,表示为n个比特的字符串,比特值为0表示不选该物品,比特值为1表示选择该物品。

(2)基因:染色体的每一个比特。

(3)种群:解的集合。

(4)适应度:衡量个体优劣的函数值。

2、控制参数(1)种群规模:解的个数。

(2)最大遗传的代数(3)交叉率:参加交叉运算的染色体个数占全体染色体的比例,取值范围一般为0.4~0.99。

(4)变异率:发生变异的基因位数所占全体染色体的基因总位数的比例,取值范围一般为0.0001~0.1。

3、算法描述(1)在搜索空间U上定义一个适应度函数f(x),给定种群规模N,交叉率Pc和变异率Pm,代数T;(2)随机产生U中的N个个体s1, s2, …, sN,组成初始种群S={s1, s2, …, sN},置代数计数器t=1;(3)计算S中每个个体的适应度f() ;(4)若终止条件满足,则取S中适应度最大的个体作为所求结果,算法结束。

(5)按选择概率P(xi)所决定的选中机会,每次从S中随机选定1个个体并将其染色体复制,共做N次,然后将复制所得的N个染色体组成群体S1;(6)按交叉率Pc所决定的参加交叉的染色体数c,从S1中随机确定c个染色体,配对进行交叉操作,并用产生的新染色体代替原染色体,得群体S2;(7)按变异率P m所决定的变异次数m,从S2中随机确定m个染色体,分别进行变异操作,并用产生的新染色体代替原染色体,得群体S3;(8)将群体S3作为新一代种群,即用S3代替S,t = t+1,转步3。

三、算法实现1、主要的数据结构染色体:用一维数组表示,数组中下标为i的元素表示第(i+1)个物品的选中状态,元素值为1,表示物品被选中,元素值为0表示物品不被选中。

种群:用二维数组表示,每一行表示一个染色体。

浅谈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背包问题的枚举算法

0-1背包问题的枚举算法

0-1背包问题的枚举算法一、问题概述0-1背包问题是一种经典的优化问题,给定一组物品,每种物品都有自己的重量和价值,而你有一个限制容量的背包。

目标是在不超过背包容量的情况下,选择物品使得总价值最大化。

然而,在某些情况下,所有的物品都不能被放入背包中,这时就需要用到0-1背包问题的枚举算法。

二、算法原理枚举算法的基本思想是从所有可能的物品组合中逐个尝试,找出满足条件的组合。

对于0-1背包问题,我们可以枚举所有可能的物品组合,对于每个组合,计算其总价值和当前背包的剩余容量,如果总价值大于当前背包容量所能获得的最大价值,那么就将这个物品放入背包中,并更新背包剩余容量和总价值。

如果当前物品的价值小于或等于当前背包容量所能获得的最大价值,那么就将这个物品标记为0(表示已经考虑过),并继续尝试下一个物品。

最终得到的组合就是最优解。

三、算法实现以下是一个简单的Python实现:```pythondefknapsack_enumeration(items,capacity):#初始化结果列表和当前价值result=[]current_value=0#枚举所有可能的物品组合foriinrange(len(items)):#标记当前物品为0(已考虑过)items[i][1]=0#计算当前物品的价值并更新总价值forjinrange(len(items)):ifj<i:#不考虑之前的物品对当前物品的价值影响current_value+=items[j][1]*items[i][0]/capacityelse:#考虑之前的物品对当前物品的价值影响(假设不考虑前一个物品的重量)current_value+=items[j][0]*(capacity-items[i][0])/capacity#将当前物品从物品列表中移除(放入背包中)delitems[i]#将当前价值添加到结果列表中result.append(current_value)returnresult```四、应用场景枚举算法在许多实际应用中都有应用,如计算机科学、运筹学、工程学等。

动态规划算法--01背包问题

动态规划算法--01背包问题

动态规划算法--01背包问题基本思想:动态规划算法通常⽤于求解具有某种最优性质的问题。

在这类问题中,可能会有许多可⾏解。

每⼀个解都对应于⼀个值,我们希望找到具有最优值的解。

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若⼲个⼦问题,先求解⼦问题,然后从这些⼦问题的解得到原问题的解。

与分治法不同的是,适合于⽤动态规划求解的问题,经分解得到⼦问题往往不是互相独⽴的(即下⼀个⼦阶段的求解是建⽴在上⼀个⼦阶段的解的基础上,进⾏进⼀步的求解)。

若⽤分治法来解这类问题,则分解得到的⼦问题数⽬太多,有些⼦问题被重复计算了很多次。

如果我们能够保存已解决的⼦问题的答案,⽽在需要时再找出已求得的答案,这样就可以避免⼤量的重复计算,节省时间。

我们可以⽤⼀个表来记录所有已解的⼦问题的答案。

不管该⼦问题以后是否被⽤到,只要它被计算过,就将其结果填⼊表中。

这就是动态规划法的基本思路。

具体的动态规划算法多种多样,但它们具有相同的填表格式。

应⽤场景:适⽤动态规划的问题必须满⾜最优化原理、⽆后效性和重叠性。

1、最优化原理(最优⼦结构性质)最优化原理可这样阐述:⼀个最优化策略具有这样的性质,不论过去状态和决策如何,对前⾯的决策所形成的状态⽽⾔,余下的诸决策必须构成最优策略。

简⽽⾔之,⼀个最优化策略的⼦策略总是最优的。

⼀个问题满⾜最优化原理⼜称其具有最优⼦结构性质。

2、⽆后效性将各阶段按照⼀定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态⽆法直接影响它未来的决策,⽽只能通过当前的这个状态。

换句话说,每个状态都是过去历史的⼀个完整总结。

这就是⽆后向性,⼜称为⽆后效性。

3、⼦问题的重叠性动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。

其中的关键在于解决冗余,这是动态规划算法的根本⽬的。

动态规划实质上是⼀种以空间换时间的技术,它在实现的过程中,不得不存储产⽣过程中的各种状态,所以它的空间复杂度要⼤于其它的算法。

贪心算法之背包问题

贪心算法之背包问题

贪⼼算法之背包问题贪⼼算法之背包问题1.与动态规划的区别通过研究解决经典的组合优化问题,来说明⼆者的差别。

即0-1背包问题与背包问题0-1背包问题:给定n中物品和⼀个背包。

物品i的重量为W i,其价值为V i,背包的容量为C。

应如何选择装⼊背包的物品,使得装⼊背包中物品的总价值最⼤?对于每种物品i只有俩种选择,即装⼊背包或不装⼊背包背包问题:与0-1背包问题类似,不同在于选择物品i装⼊背包时,可以选择物品i的⼀部分,⽽不⼀定要全部装⼊背包,1≤i≤n。

这2类问题都具有最优⼦结构性质,极为相似。

背包问题可以⽤贪⼼算法求最优解,0-1背包不能使⽤贪⼼求解。

2.贪⼼解决背包问题步骤贪⼼策略:每次选择单位重量价值最⾼的物品装⼊背包计算每种物品单位重量的价值V iW i,按单位重量的价值从⼤到⼩将n中物品排序。

以排序后的次序依次将物品装⼊背包。

直⾄全部物品都装⼊或者因背包容量不⾜不能装⼊为⽌如果背包尚有容量,将最后不能完全装⼊物品切割⼀部分装满背包算法结束3.代码实现/*** n 物品数* M 背包容量* v[] 物品价值数组* w[] 物品重量数组* x[] 保存最优解路径数组,为1则表⽰该物品完全装⼊,否则装⼊该物品的⼀部分**/void Knapsack(int n, float M, float v[], float w[], float x[]) {// 按照物品单位重量的价值递减排序Sort(n, v, w);int i;for (i = 1; i <= n; i++)x[i] = 0;float c = M;for (i = 1; i <= n; i++) {if (w[i] > c)break;x[i] = 1;c -= w[i];}if (i <= n)x[i] = c / w[i];}Processing math: 100%。

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

一、实验目的与要求掌握回溯法、分支限界法的原理,并能够按其原理编程实现解决0-1背包问题,以加深对回溯法、分支限界法的理解。

1.要求分别用回溯法和分支限界法求解0-1背包问题;2.要求交互输入背包容量,物品重量数组,物品价值数组;3.要求显示结果。

二、实验方案在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。

不能将物品i装入背包多次,也不能只装入部分的物品i。

三、实验结果和数据处理1.用回溯法解决0-1背包问题:代码:import java.util.*;public class Knapsack{private double[] p,w;//分别代表价值和重量private int n;private double c,bestp,cp,cw;private int x[]; //记录可选的物品private int[] cx;public Knapsack (double pp[],double ww[],double cc){this.p=pp;this.w=ww;this.n=pp.length-1;this.c=cc;this.cp=0;this.cw=0;this.bestp=0;x=new int[ww.length];cx=new int[pp.length];}void Knapsack(){backtrack(0);}void backtrack(int i){if(i>n){ //判断是否到达了叶子节点if(cp>bestp){for(int j=0;j<x.length;j++)x[j]=cx[j];bestp=cp;}return;}if(cw+w[i]<=c){//搜索右子树cx[i]=1;cw+=w[i];cp+=p[i];backtrack(i+1);cw-=w[i];cp-=p[i];}cx[i]=0;backtrack(i+1); //检查左子树}void printResult(){System.out.println("回溯法");System.out.println("物品个数:n=4");System.out.println("背包容量:c=7");System.out.println("物品重量数组:w= {3,5,2,1}");System.out.println("物品价值数组:p= {9,10,7,4}");System.out.println("最优值:="+bestp);System.out.println("选中的物品是:");for(int i=0;i<x.length;i++){System.out.print(x[i]+" ");}}public static void main(String[] args){double p[]={9,10,7,4};double w[]={3,5,2,1};int maxweight=7;Knapsack ks=new Knapsack(p,w,maxweight);ks.Knapsack(); //回溯搜索ks.printResult();}}运行结果:2.用优先队列式分支限界法解决0-1背包问题:代码:public class Knapsack{static double c;static int n;static double w[];static double p[];static double cw;static double cp;static int bestX[];static MaxHeap heap;//上界函数bound计算结点所相应价值的上界private static double bound(int i){double cleft=c-cw;double b=cp;while(i<=n&&w[i]<=cleft){cleft=cleft-w[i];b=b+p[i];i++;}//装填剩余容量装满背包if(i<=n)b=b+p[i]/w[i]*cleft;return b;}//addLiveNode将一个新的活结点插入到子集树和优先队列中private static void addLiveNode(double up,double pp,double ww,int lev,BBnode par,boolean ch) {//将一个新的活结点插入到子集树和最大堆中BBnode b=new BBnode(par,ch);HeapNode node =new HeapNode(b,up,pp,ww,lev);heap.put(node);}private static double MaxKnapsack(){//优先队列式分支限界法,返回最大价值,bestx返回最优解BBnode enode=null;int i=1;double bestp=0;//当前最优值double up=bound(1);//当前上界while(i!=n+1){//非叶子结点//检查当前扩展结点的左儿子子结点double wt=cw+w[i];if(wt<=c){if(cp+p[i]>bestp)bestp=cp+p[i];addLiveNode(up,cp+p[i],cw+w[i],i+1,enode,true);}up=bound(i+1);if(up>=bestp)addLiveNode(up,cp,cw,i+1,enode,false);HeapNode node =(HeapNode)heap.removeMax();enode=node.liveNode;cw=node.weight;cp=node.profit;up=node.upperProfit;i=node.level;}for(int j=n;j>0;j--){bestX[j]=(enode.leftChild)?1:0;enode=enode.parent;}return cp;}public static double Knapsack(double pp[],double ww[],double cc,int xx[]) {//返回最大值,bestX返回最优解c=cc;n=pp.length-1;//定义以单位重量价值排序的物品数组Element q[]=new Element[n];double ws=0.0;double ps=0.0;for(int i=0;i<n;i++){q[i]=new Element(i+1,pp[i+1]/ww[i+1]);ps=ps+pp[i+1];ws=ws+ww[i+1];}if(ws<=c){return ps;}p=new double[n+1];w=new double[n+1];for(int i=0;i<n;i++){p[i+1]=pp[q[i].id];w[i+1]=ww[q[i].id];}cw=0.0;cp=0.0;bestX = new int[n+1];heap = new MaxHeap(n);double bestp = MaxKnapsack();for(int j=0;j<n;j++)xx[q[j].id]=bestX[j+1];return bestp;}public static void main(String [] args){double w[]=new double[5];w[1]=3;w[2]=5;w[3]=2;w[4]=1;double p[]=new double[5];p[1]=9;p[2]=10;p[3]=7;p[4]=4;double c=7;int x[] = new int[5];double m = Knapsack(p,w,c,x);System.out.println("优先队列式分支限界法:");System.out.println("物品个数:n=4");System.out.println("背包容量:c=7");System.out.println("物品重量数组:w= {3,5,2,1}");System.out.println("物品价值数组:p= {9,10,7,4}");System.out.println("最优值:="+m);System.out.println("选中的物品是:");for(int i=1;i<=4;i++)System.out.print(x[i]+" ");}}//子空间中节点类型class BBnode{BBnode parent;//父节点boolean leftChild;//左儿子节点标志BBnode(BBnode par,boolean ch){parent=par;leftChild=ch;}}class HeapNode implements Comparable{BBnode liveNode; // 活结点double upperProfit; //结点的价值上界double profit; //结点所相应的价值double weight; //结点所相应的重量int level; // 活结点在子集树中所处的层次号//构造方法public HeapNode(BBnode node, double up, double pp , double ww,int lev) {liveNode = node;upperProfit = up;profit = pp;weight = ww;level = lev;}public int compareTo(Object o){double xup = ((HeapNode)o).upperProfit;if(upperProfit < xup)return -1;if(upperProfit == xup)return 0;elsereturn 1;}}class Element implements Comparable{int id;double d;public Element(int idd,double dd){id=idd;d=dd;}public int compareTo(Object x){double xd=((Element)x).d;if(d<xd)return -1;if(d==xd)return 0;return 1;}public boolean equals(Object x){return d==((Element)x).d;}}class MaxHeap{static HeapNode [] nodes;static int nextPlace;static int maxNumber;public MaxHeap(int n){maxNumber = (int)Math.pow((double)2,(double)n);nextPlace = 1;//下一个存放位置nodes = new HeapNode[maxNumber];}public static void put(HeapNode node){nodes[nextPlace] = node;nextPlace++;heapSort(nodes);}public static HeapNode removeMax(){HeapNode tempNode = nodes[1];nextPlace--;nodes[1] = nodes[nextPlace];heapSort(nodes);return tempNode;}private static void heapAdjust(HeapNode [] nodes,int s,int m){HeapNode rc = nodes[s];for(int j=2*s;j<=m;j*=2){if(j<m&&nodes[j].upperProfit<nodes[j+1].upperProfit)++j;if(!(rc.upperProfit<nodes[j].upperProfit))break;nodes[s] = nodes[j];s = j;}nodes[s] = rc;}private static void heapSort(HeapNode [] nodes){for(int i=(nextPlace-1)/2;i>0;--i){heapAdjust(nodes,i,nextPlace-1);}}}运行结果:3.用队列式分支限界法解决0-1背包问题:代码:#include<stdio.h>#include<stdlib.h>#define MAXNUM 100struct node{int step;double price;double weight;double max, min;unsigned long po;};typedef struct node DataType;struct SeqQueue{ /* 顺序队列类型定义*/int f, r;DataType q[MAXNUM];};typedef struct SeqQueue *PSeqQueue;PSeqQueue createEmptyQueue_seq( void ){PSeqQueue paqu;paqu = (PSeqQueue)malloc(sizeof(struct SeqQueue));if (paqu == NULL)printf("Out of space!! \n");elsepaqu->f = paqu->r = 0;return paqu;}int isEmptyQueue_seq( PSeqQueue paqu ){return paqu->f == paqu->r;}/* 在队列中插入一元素x */void enQueue_seq( PSeqQueue paqu, DataType x ){if((paqu->r + 1) % MAXNUM == paqu->f)printf( "Full queue.\n" );else{paqu->q[paqu->r] = x;paqu->r = (paqu->r + 1) % MAXNUM;}}/* 删除队列头元素*/void deQueue_seq( PSeqQueue paqu ){if( paqu->f == paqu->r )printf( "Empty Queue.\n" );elsepaqu->f = (paqu->f + 1) % MAXNUM;}/* 对非空队列,求队列头部元素*/DataType frontQueue_seq( PSeqQueue paqu ){return (paqu->q[paqu->f]);}/* 物品按性价比从新排序*/void sort(int n, double p[], double w[]){int i, j;for (i = 0; i < n-1; i++)for (j = i; j < n-1; j++){double a = p[j]/w[j];double b = p[j+1]/w[j+1];if (a < b){double temp = p[j];p[j] = p[j+1];p[j+1] = temp;temp = w[j];w[j] = w[j+1];w[j+1] = temp;}}}/* 求最大可能值*/double up(int k, double m, int n, double p[], double w[]) {int i = k;double s = 0;while (i < n && w[i] < m){m -= w[i];s += p[i];i++;}if (i < n && m > 0){s += p[i] * m / w[i];i++;}return s;}/* 求最小可能值*/double down(int k, double m, int n, double p[], double w[]){int i = k;double s = 0;while (i < n && w[i] <= m){m -= w[i];s += p[i];i++;}return s;}/* 用队列实现分支定界算法*/double solve(double m, int n, double p[], double w[], unsigned long* po) {double min;PSeqQueue q = createEmptyQueue_seq();DataType x = {0,0,0,0,0,0};sort(n, p, w);x.max = up(0, m, n, p, w);x.min = min = down(0, m, n, p, w);if (min == 0) return -1;enQueue_seq(q, x);while (!isEmptyQueue_seq(q)){int step;DataType y;x = frontQueue_seq(q);deQueue_seq(q);if (x.max < min) continue;step = x.step + 1;if (step == n+1) continue;y.max = x.price + up(step, m - x.weight, n, p, w);if (y.max >= min){y.min = x.price + down(step, m-x.weight, n, p, w);y.price = x.price;y.weight = x.weight;y.step = step;y.po = x.po << 1;if (y.min >= min){min = y.min;if (step == n) *po = y.po;}enQueue_seq(q, y);}if (x.weight+w[step-1]<= m){y.max = x.price + p[step-1]+up(step, m-x.weight-w[step-1], n, p, w);if (y.max >= min) {y.min = x.price + p[step-1] +down(step, m-x.weight-w[step-1], n, p, w);y.price = x.price + p[step-1];y.weight = x.weight + w[step-1];y.step = step;y.po = (x.po << 1) + 1;if (y.min >= min){min = y.min;if (step == n) *po = y.po;}enQueue_seq(q, y);}}}return min;}#define n 4double m = 7;double p[n] = {9, 10, 7, 4};double w[n] = {3, 5, 1, 2};int main(){int i;double d;unsigned long po;d = solve(m, n, p, w, &po);if (d == -1)printf("No solution!\n");else{for (i = 0; i < n; i++)printf("x%d 为%d\n", i + 1, ((po & (1<<(n-i-1))) != 0));printf("最优值是:%f\n", d);}getchar();return 0;}运行结果:。

相关文档
最新文档