0036算法笔记——【分支限界法】0-1背包问题

合集下载

分枝限界法解0-1背包问题

分枝限界法解0-1背包问题

#include <iostream.h>class Knap;class Object;class Object{friend int Knapsack(int *,int *,int ,int ,int *); public:int operator <=(Object a)const{return (d >= a.d);}private:int ID;float d;//单位重量价值};class bbnode{friend Knap;friend int Kanpsack(int *,int *,int ,int ,int *); private:bbnode * parent;//指向父节点的指针bool LChild; //左儿子结点标志};class HeapNode{friend Knap;public:operator int () const{ return uprofit;}void Insert(HeapNode N);void DeleteMax(HeapNode N);private:int uprofit, //结点的价值上界profit; //结点所对应的价值int weight; //结点所对应的重量int level; //活结点在子集树中所处的层序号bbnode * ptr; //指向活结点在子集树中相应结点的指针};void HeapNode::Insert(HeapNode N){void HeapNode::DeleteMax(HeapNode N){}class Knap{friend int Knapsack(int *,int *,int ,int ,int *); public:int MaxKnapsack();private:HeapNode *H;int MaxBoundary(int i);void AddLiveNode(int up,int cp,int cw,bool ch,int level);bbnode * E; //指向扩展结点的指针int c; //背包容量int n; //物品总数int *w; //物品重量数组int *p; //物品价值数组int cw; //当前背包重量int cp; //当前背包价值int * bestx; //最优解的记录数组};//计算所相应的价值的上界int Knap::MaxBoundary(int i){int cleft=c-cw; //剩余容量int b=cp; //价值上限//以物品单位重量价值递减序装填剩余容量while(i<=n&&w[i]<=cleft){cleft-=w[i];b+=p[i];i++;}//将背包的剩余容量装满if(i<=n)b+=p[i]/w[i]*cleft;return b;}//将一个新的活结点插入到子集树和优先队列中void Knap::AddLiveNode(int up,int cp,int cw,bool ch,int lev)//将一个新的活结点插入到子集树和最大堆H中bbnode * b=new bbnode;b->parent=E;b->LChild=ch;HeapNode N;N.uprofit=up;N.profit=cp;N.weight=cw;N.level=lev;N.ptr=b;H->Insert(N);}//实施对子集树的优先队列式分支界限搜索int Knap::MaxKnapsack(){//优先队列式分支界限法,返回最大值,bestx返回最优解//定义最大堆的容量为1000H=new HeapNode [1000];//为bestx分配存储空间bestx=new int [n+1];//初始化int i=1;E=0;cw=cp=0;int bestp=0; //当前最优解int up=MaxBoundary(1);//价值上界//搜索子集空间树while(i!=n+1)//非叶结点{//检查当前扩展结点的左儿子结点int wt=cw+w[i];if(wt<=c){if(cp+p[i]>bestp)bestp=cp+p[i];AddLiveNode(up,cp+p[i],cw+w[i],true,i+1);}up=MaxBoundary(i+1);//检查当前扩展结点的右儿子结点if(up>=bestp)AddLiveNode(up,cp,cw,false,i+1);//去下一个扩展结点HeapNode N;H->DeleteMax(N);E=N.ptr;cw=N.weight;cp=N.profit;up=N.uprofit;i=N.level;}//构造当前最优解for(int j=n;j>0;j--){bestx[j]=E->LChild;E=E->parent;}return cp;}//对数据进行预处理并完成调用MaxKnapsackint Knapsack(int p[],int w[],int c,int n,int bestx[]) {//返回最大值,bestx返回最优解//初始化int W=0; //装包物品重量int P=0; //装包物品价值//定义依单位重量价值排序的物品数组Object * Q=new Object[n];for(int i=1;i<=n;i++){//单位重量价值数组Q[i-1].ID=i;Q[i-1].d=(float)1.0*p[i]/w[i];P+=p[i];W+=w[i];}if(W<=c) return P;//所有物品装包//依单位重量价值排序float f;for( i=0;i<n;i++)for(int j=i;j<n;j++){if(Q[i].d<Q[j].d){f=Q[i].d;Q[i].d=Q[j].d;Q[j].d=f;}}//创建类Knap的数据成员Knap K;K.p=new int [n+1];K.w=new int [n+1];for(i=1;i<=n;i++){K.p[i]=p[Q[i-1].ID];K.w[i]=w[Q[i-1].ID];}K.cp=0;K.cw=0;K.c=c;K.n=n;//调用MaxKnapsack求问题的最优解int bestp=K.MaxKnapsack();for(int j=1;j<=n;j++)bestx[Q[j-1].ID]=K.bestx[j];delete []Q;delete []K.w;delete []K.p;delete []K.bestx;return bestp;}void main(){int m,n;int i=0,j=0;int p[100],w[20],x[20];while(1){cout<<"0-1背包问题——递归法"<<endl;cout<<"请输入背包的容量(m)和物品个数(n):"<<endl;cin>>m>>n;cout<<"请输入每个物品的重量(w)和物品的价值(p):"<<endl;for(i=1;i<=n;i++)cin>>w[i]>>p[i];cout<<"背包的最优解为:"<<endl<<Knapsack(p,w,m,n,x)<<endl;cout<<"最优解条件下选择的背包为:"<<endl;for(i=1;i<=n;i++)cout<<x[i]<<"\t";cout<<endl;}}。

0-1背包问题

0-1背包问题

0/1背包问题一、问题描述已知一个容量为M的背包和n件物品,物品编号为0~n-1,第i件物品的重量为w i,若将其装入背包将获益p i。

这里w i>0, p i >0(0≤i<n)。

所谓的0/1背包问题,是指在物品只能整件装入或不装入的情况下,求一种使得总效益最大的装载方案。

上述问题可形式化描述为:给定M>0,w i>0, p i>0(0≤i<n),求一个n元向量x=(x0,x1,…,x n-1),x i∈{0,1}(0≤i<n)使得∑<≤ni0w i x i≤M且∑<≤nip i x i最大。

为了叙述方便将该问题简记为KNAP(0,n-1,M)。

二、递归法求解1.分析已知x i∈{0,1},0≤i<n,假定对x i 作决策的次序是x=(x n-1,x n-2,…,x0),在对x n-1作出决策时存在以下两种情况:(1)x n-1=1,将编号为n-1的物品装入包中,接着求解子问题KNAP(0,n-2,M-w n-1);(2)x n-1=0,不将编号为n-1的物品装入包中,接着求解子问题KNAP(0,n-2,M)。

设f(j,X)是当背包容量为X ,可供选择的物品为0,1,…,j 时的最优解(即最大总效益),那么f(n-1,M)可表示为:f(n-1,M)=max{f(n-2,M),f(n-2,M-w n-1)+p n-1}对于任意的j ,0≤j <n,有f(j,X)=max{f(j-1,X),f(j-1,X-w j )+p j } 若将物品j 装入包中,则f(j,X)= f(j-1,X-w j )+p j反之f(j,X)= f(j-1,X)2.算法(1)f(-1,X)=⎩⎨⎧≥<∞-)0(0)0(X X ;(2)f(j,X)= max{f(j-1,X),f(j-1,X-w j )+p j } 其中0≤j <n 。

3.考虑以下背包问题,n=3, (w 0,w 1,w 2)=(2,3,4),(p 0,p 1,p 2)=(1,2,5)和M=6。

分支限界法结局0~1背包问题

分支限界法结局0~1背包问题

Bound( i ) cleft = c – cw; b = cp; while( i <= n && w[i] <= cleft ){ cleft -= w[i]; b += p[i]; i++; } if( i<=n) b += p[i]/w[i] * cleft; return b; }

此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结 点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为 止。
与回溯法区别
求解目标不同: 一般而言,回溯法的求解目标是找出解空间树中满 足的约束条件的所有解,而分支限界法的求解目标 则是尽快的找出满足约束条件的一个解。

搜索方法不同 回溯法使用深度优先方法搜索,而分支限界一般用宽 度优先或最佳优先方法来搜索;

按照队列先进先出(FIFO)原则选取下一个节点为扩展节点;
数据结构:队列
(2)优先队列式分支限界法

按照优先队列中规定的优先级选取优先级最高的节点成为当前 扩展节点。 数据结构:堆 最大优先队列:使用最大堆,体现最大效益优先

最小优先队列:使用最小堆,体现最小费用优先
【0-1背包问题】
物品数量n=3,重量w=(20,15,15),价值v=(40,25,25) 背包容量c=30,试装入价值和最大的物品? 解空间:{(0,0,0),(0,0,1),…,(1,1,1)}
分支限界法解决0/1背包问题
分支限界法思想概述 与回溯法区别 求解步骤 常见的两种分支限界法 0-1背包问题
分支限界法的基本思想
分支限界法基本思想

分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜 索问题的解空间树。

实验报告分支限界法01背包

实验报告分支限界法01背包

实验报告分支限界法01背包实验报告:分支限界法解决01背包问题一、引言背包问题是数学和计算机科学中一个经典的问题。

背包问题通常分为01背包问题和完全背包问题两种情况。

本实验主要探讨的是分支限界法解决01背包问题,该算法常用于解决NP难问题。

分支限界法通过将问题分解为一系列子问题,并借助剪枝技术,逐步缩小问题的空间,从而找到最优解。

本实验将通过具体的案例来展示分支限界法的求解过程和原理,并对算法的时间复杂度和空间复杂度进行分析。

二、算法原理01背包问题的数学模型为:有n个物品,每个物品有一个重量wi和一个价值vi,在限定的背包容量为W的情况下,如何选择物品放入背包,使得背包中物品的总价值最大。

分支限界法的基本思想是:通过不断地分解问题为更小的子问题,并使用估算函数对子问题进行优先级排序,将优先级最高的子问题优先求解。

具体步骤如下:1.根节点:将背包容量W和物品序号0作为初始状态的根节点。

2.扩展节点:对于任意一个节点S,选择装入下一个物品或者不装入两种分支。

计算新节点的上界。

3.优先级队列:将扩展节点按照上界从大到小的顺序插入优先级队列。

4.剪枝条件:当扩展节点的上界小于当前已找到的最优解时,可以剪枝。

5.结束条件:当到叶节点或者队列为空时,结束。

若叶节点的上界高于当前最优解,更新最优解。

三、实验过程1.输入数据:给定一个物品序列,每个物品有重量和价值,以及一个背包的最大容量。

2.算法实现:根据算法原理,使用编程语言实现分支限界法的求解过程。

3.结果分析:比较算法求解得到的最优解和其他算法(如动态规划)得到的最优解之间的差异。

四、实验结果以一个具体的案例来说明分支限界法的求解过程。

假设有4个物品,其重量和价值分别为{2,3,4,5}和{3,4,5,6},背包的最大容量为8、通过分支限界法求解,得到最优解为9,对应的物品选择为{2,3,5}。

通过与动态规划算法的结果比较,可以发现分支限界法的最优解与动态规划算法得到的最优解是一致的。

分支界限法0-1背包问题(优先队列式分支限界法)

分支界限法0-1背包问题(优先队列式分支限界法)

分⽀界限法0-1背包问题(优先队列式分⽀限界法)输⼊要求有多组数据。

每组数据包含2部分。

第⼀部分包含两个整数C (1 <= C <= 10000)和 n (1 <= n <= 10,分别表⽰背包的容量和物品的个数。

第⼆部分由n⾏数据,每⾏包括2个整数 wi(0< wi <= 100)和 vi(0 < vi <= 100),分别表⽰第i个物品的总量和价值输出要求对于每组输⼊数据,按出队次序输出每个结点的信息,包括所在层数,编号,背包中物品重量和价值。

每个结点的信息占⼀⾏,如果是叶⼦结点且其所代表的背包中物品价值⼤于当前最优值(初始为0),则输出当前最优值 bestv 和最优解bestx(另占⼀⾏)参见样例输出测试数据输⼊⽰例5 32 23 22 3输出⽰例1 1 0 02 2 2 23 5 2 24 10 4 5bestv=5, bestx=[ 1 0 1 ]4 11 2 23 4 5 42 3 0 0⼩贴⼠可采⽤如下的结构体存储结点:typedef struct{int no; // 结点在堆中的标号int sw; // 背包中物品的重量int sv; // 背包中物品的价值double prior; // 优先值 sv/sw}Node;#include<stdio.h>#include<math.h>#include<string.h>typedef struct {int no; // 结点标号int id; // 节点idint sw; // 背包中物品的重量int sv; // 背包中物品的价值double prior; // sv/sw}Node;int surplusValue(int *v,int n,int y) {int sum = 0;for(int i = y; i <= n; i++) {sum += v[i];}return sum;}void qsort(Node *que,int l,int r) {int len = r - l + 1;int flag;for(int i = 0; i < len; i ++) {flag = 0;for(int j = l; j < l + len - i; j++) {if(que[j].prior < que[j+1].prior) {Node t = que[j];que[j] = que[j+1];que[j+1] = t;flag = 1;}}//if(!flag ) return;}}void branchknap(int *w,int *v,int c,int n) {int bestv = 0;int f = 0;int r = 0;Node que[3000];memset(que,0,sizeof(que));int path[15];que[0].no = 1;que[0].id = que[0].sv = que[0].sw = que[0].prior = 0;while(f <= r) {Node node = que[f];printf("%d %d %d %d\n",node.id+1,node.no,node.sw,node.sv);if(node.no >= pow(2,n)) {if(node.sv > bestv) {bestv = node.sv;printf("bestv=%d, bestx=[",bestv);int temp = node.no;int i = 0;while(temp > 1) {if(temp % 2 == 0)path[i] = 1;elsepath[i] = 0;temp /= 2;i++ ;}i--;while(i >= 0) {while(i >= 0) {printf(" %d",path[i]);i--;}printf(" ]\n");}} else {if((node.sw + w[node.id + 1]) <= c && surplusValue(v,n,node.id+1) + node.sv > bestv) { r++;que[r].id = node.id + 1;que[r].no = node.no*2;int id = node.id + 1;que[r].sv = node.sv + v[id];que[r].sw = node.sw + w[id];que[r].prior = que[r].sv / (que[r].sw*1.0);}if(surplusValue(v,n,node.id+2) + node.sv > bestv) {r++;que[r].id = node.id + 1;que[r].no = node.no*2 + 1;que[r].sv = node.sv;que[r].sw = node.sw;que[r].prior = node.prior;}}f++;qsort(que,f,r);}}int main() {int c,n;int w[15],v[15];while(~scanf("%d %d",&c,&n)){for(int i = 1; i <= n; i++) {scanf("%d %d",&w[i],&v[i]);}branchknap(w,v,c,n);}return 0;}#include<stdio.h>#include<math.h>#include<string.h>typedef int bool;#define true 1#define false 0struct Node{int no; // ?áµ?±êo?int id; //jie dian idint sw; // ±3°ü?D·µá?int sv; // ±3°ü?D·µ?µdouble prior;};struct Node queuee[2000];int w[15],v[15];int bestv = 0,c,n;int path[15]; //lu jingint surplusValue(int y) {int sum = 0;for(int i = y; i <= n; i++)sum += v[i];return sum;}void qsort(int l,int r) {// printf("------\n");int len = r - l + 1;//printf("----%d %d %d-----\n",l,r,len);bool flag;for(int i = 0; i < len ; i++) {flag = false;for(int j = l; j <l+ len -i ;j++) {if(queuee[j].prior < queuee[j+1].prior) {struct Node temp = queuee[j];queuee[j] = queuee[j+1];queuee[j+1] = temp;flag = true;}//if(!flag) return;}}// printf("---排序嘻嘻---\n");//for(int i = l; i <= r;i++ )// printf("***%d : %.2lf\n",queuee[i].no,queuee[i].prior);// printf("\n------\n");}void branchknap() {bestv = 0;int f = 0;int r = 0;queuee[0].no = 1;queuee[0].id = 0;queuee[0].sv = 0;queuee[0].sw = 0;queuee[0].prior = 0;// printf("f: %d r: %d\n",f,r);while(f <= r) {struct Node node = queuee[f];printf("%d %d %d %d\n",node.id+1,node.no,node.sw,node.sv);if(node.no >= pow(2,n)) {if(node.sv > bestv) {bestv = node.sv;//TODOprintf("bestv=%d, bestx=[",bestv);int temp = node.no;int i = 0;while(temp > 1) {if(temp%2 == 0)path[i] = 1;elsepath[i] = 0;temp /= 2;i++;}i--;while(i >= 0) {while(i >= 0) {printf(" %d",path[i]);i--;}printf(" ]\n");}} else {if((node.sw + w[node.id+1]) <= c && surplusValue(node.id+1) + node.sv > bestv) { r++;//printf("%d\n",(node.sw + w[node.id+1]));queuee[r].id = node.id+1;queuee[r].no = node.no*2;int id = node.id+1;queuee[r].sv = node.sv + v[id];queuee[r].sw = node.sw + w[id];queuee[r].prior = queuee[r].sv/(queuee[r].sw*1.0);//printf("进队id: %d\n",queuee[r].no) ;//printf("%d %d %d\n",id,v[id], w[id]);}if(surplusValue(node.id+2) + node.sv > bestv) {r++;queuee[r].id = node.id+1;queuee[r].no = node.no*2 + 1;queuee[r].sv = node.sv ;queuee[r].sw = node.sw ;queuee[r].prior = node.prior;//printf("进队id: %d\n",queuee[r].no) ;}}f++;qsort(f,r);}}int main() {while(~scanf("%d %d",&c,&n)){memset(queuee,0,sizeof(queuee));for(int i = 1; i <= n; i++) {scanf("%d %d",&w[i],&v[i]);}branchknap();}return 0;}。

分支界限方法01背包问题解题步骤

分支界限方法01背包问题解题步骤

分支界限方法是一种用于解决优化问题的算法。

在动态规划算法中,分支界限方法被广泛应用于解决01背包问题。

01背包问题是一个经典的动态规划问题,其解题步骤如下:1. 确定问题:首先需要明确01背包问题的具体描述,即给定一组物品和一个背包,每个物品有自己的价值和重量,要求在不超过背包容量的情况下,选取尽可能多的物品放入背包,使得背包中物品的总价值最大。

2. 列出状态转移方程:对于01背包问题,可以通过列出状态转移方程来描述问题的求解过程。

假设dp[i][j]表示在前i个物品中,背包容量为j时能够获得的最大价值,则状态转移方程可以表示为:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])3. 初始化边界条件:在动态规划中,需要对状态转移方程进行初始化,一般情况下,dp数组的第一行和第一列需要单独处理。

对于01背包问题,可以初始化dp数组的第一行和第一列为0。

4. 利用分支界限方法优化:针对01背包问题,可以使用分支界限方法来优化动态规划算法的效率。

分支界限方法采用广度优先搜索的思想,在每一步选择最有希望的分支,从而减少搜索空间,提高算法的效率。

5. 实际解题步骤:根据上述步骤,实际解决01背包问题的步骤可以概括为:确定问题,列出状态转移方程,初始化边界条件,利用分支界限方法优化,最终得到问题的最优解。

分支界限方法在解决01背包问题时起到了重要的作用,通过合理的剪枝策略,可以有效地减少动态规划算法的时间复杂度,提高问题的求解效率。

分支界限方法也可以应用于其他优化问题的求解过程中,在算法设计和实现中具有重要的理论和实际意义。

在实际应用中,分支界限方法需要根据具体问题进行灵活选择和调整,结合动态规划和剪枝策略,以便更好地解决各类优化问题。

掌握分支界限方法对于解决复杂问题具有重要的意义,也是算法设计和优化的关键技术之一。

分支界限方法在解决01背包问题的过程中,具有重要的作用。

优先队列式分支限界法求解0-1背包问题

优先队列式分支限界法求解0-1背包问题

算法分析与设计实验报告第7 次实验}1、测试自己输入的小规模数据2、测试随机生成1003、随机生成1000数据4、随机生成1000数据附录:完整代码#include <iostream>#include<time.h>#include<algorithm>#include<fstream>using namespace std;ifstream in("input.txt");ofstream out("output.txt");typedef int Typew;typedef int Typep;//物品类class Object{friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); public:int operator <= (Object a) const{return (d >= a.d);}private:int ID; //物品编号float d; //单位重量价值};//树结点类class bbnode{friend class Knap;friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); private:bbnode *parent; //指向父节点的指针int LChild;};//堆结点类class HeapNode{friend class Knap;friend class MaxHeap;public:operator Typep()const{return uprofit;};private:Typep uprofit, //结点的价值上界profit; //结点所相应的价值Typew weight; //结点所相应的重量int level; //活结点在子集树中所处的层序号bbnode *elemPtr; //指向该活结点在子集树中相应结点的指针};//最大堆类class MaxHeap{public:MaxHeap(int maxElem){HeapElem = new HeapNode* [maxElem+1]; //下标为0的保留capacity = maxElem;size = 0;}void InsertMax(HeapNode *newNode);HeapNode DeleteMax(HeapNode* &N);private:int capacity;int size;HeapNode **HeapElem;};//0-1背包问题的主类class Knap{friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); public:Typep MaxKnapsack();private:MaxHeap *H;Typep Bound(int i);void AddLiveNode(Typep up, Typep cp, Typew cw, int ch, int level);bbnode *E; //指向扩展结点的指针Typew c; //背包容量int n; //物品总数Typew *w; //物品重量数组(以单位重量价值降序)Typep *p; //物品价值数组(以单位重量价值降序)Typew cw; //当前装包重量Typep cp; //当前装包价值int *bestx; //最优解};void MaxHeap::InsertMax(HeapNode *newNode){int i = 1;for (i = ++size; i/2 > 0 && HeapElem[i/2]->uprofit < newNode->uprofit; i /= 2){HeapElem[i] = HeapElem[i/2];}HeapElem[i] = newNode;}HeapNode MaxHeap::DeleteMax(HeapNode *&N){if(size >0 ){N = HeapElem[1];int i = 1;while(i < size){if(((i*2 +1) <= size) && HeapElem[i*2]->uprofit > HeapElem[i*2 +1]->uprofit){HeapElem[i] = HeapElem[i*2];i = i*2;}else{if(i*2 <= size){HeapElem[i] = HeapElem[i*2];i = i*2;}elsebreak;}}if(i < size)HeapElem[i] = HeapElem[size];}size--;return *N;}Typep Knap::MaxKnapsack(){H = new MaxHeap(10000);bestx = new int [n+1];int i = 1;E = 0;cw = 0;cp = 0;Typep bestp = 0;Typep up = Bound(1);while (i != n+1){Typew wt = cw + w[i];if(wt <= c) {if(cp + p[i] > bestp)bestp = cp + p[i];AddLiveNode(up, cp + p[i], cw + w[i], 1, i);}up = Bound(i + 1);if(up >= bestp)AddLiveNode(up, cp, cw, 0, i);HeapNode* N;H->DeleteMax(N);E = N->elemPtr;cw = N->weight;cp = N->profit;up = N->uprofit;i = N->level + 1;}for (int i = n; i > 0; i--){bestx[i] = E->LChild;E = E->parent;}return cp;}Typep Knap::Bound(int i){Typew cleft = c - cw;Typep b = cp;while (i<=n && w[i] <= cleft){cleft -= w[i];b += p[i];i++;}if(i<=n) b += p[i]/w[i] * cleft;return b;}void Knap::AddLiveNode(Typep up, Typep cp, Typew cw, int ch, int level) {bbnode *b=new bbnode;b->parent=E;b->LChild=ch;HeapNode *N = new HeapNode;N->uprofit=up;N->profit=cp;N->weight=cw;N->level=level;N->elemPtr=b;H->InsertMax(N);}//Knapsack返回最大价值,最优值保存在bestxTypep Knapsack(Typew *w, Typep *p, Typew c, int n, int *bestx){Typew W = 0;Typep P = 0;Object *Q = new Object[n];for(int i =1; i<=n; i++){Q[i-1].ID = i;Q[i-1].d = 1.0*p[i]/w[i];P += p[i];W += w[i];}if (W <= c){for(int i =1; i<=n; i++){bestx[i] = p[i];}return P;}for(int i = 1; i<n; i++)for(int j = 1; j<= n-i; j++){if(Q[j-1].d < Q[j].d){Object temp = Q[j-1];Q[j-1] = Q[j];Q[j] = temp;}}Knap K;K.p = new Typep [n+1];K.w = new Typew [n+1];for(int i = 1; i<=n; i++){K.p[i] = p[Q[i-1].ID];K.w[i] = w[Q[i-1].ID];}K.cp = 0;K.cw = 0;K.c = c;K.n = n;Typep bestp = K.MaxKnapsack();for(int i = 1; i<=n; i++){bestx[Q[i-1].ID] = K.bestx[i];}delete [] Q;delete [] K.w;delete [] K.p;delete [] K.bestx;delete [] K.H;return bestp;}int main(){cout<<"请在input.txt文件中输入物品数量、背包容量"<<endl;int N ;in>>N;Typew c; //背包容量in>>c;int bestx[N+1]; //最优解int bestp; //最优值Typep p[N+1];//物品价值Typew w[N+1];//物品重量cout<<"在input.txt文件中读取的物品总数N = "<< N<<",背包容量C = "<< c<<endl; cout<<"请选择生成数据的规模大小:200请输入1,2000请输入2,20000请输入3"<<endl; int x;cin>>x;if(x==1){ofstream in1("input1.txt");srand(time(NULL));int n=200;int *a=new int[n];for(int i=0;i<n;i++){a[i]=rand()%91;in1<<a[i]<<" ";}cout<<"随机数已请生成到input1文件中,请将数据添加到input.txt文件中"<<endl; }else if(x==2){ofstream in1("input1.txt");srand(time(NULL));int n=2000;int *a=new int[n];for(int i=0;i<n;i++){a[i]=rand()%91;in1<<a[i]<<" ";}cout<<"随机数已请生成到input1文件中,请将数据添加到input.txt文件中"<<endl; }else if(x==3){ofstream in1("input1.txt");srand(time(NULL));int n=20000;int *a=new int[n];for(int i=0;i<n;i++){a[i]=rand()%91;in1<<a[i]<<" ";}cout<<"随机数已请生成到input1文件中,请将数据添加到input.txt文件中"<<endl;}cout<<"添加完毕后请输入1"<<endl;int m;cin>>m;clock_t start,finish;start=clock();for (int i = 1; i <= N; i++){in>>w[i];}for (int i = 1; i <= N; i++){in>>p[i];}cout<<"已在input文件中读取物品重量和价值。

0036算法笔记——【分支限界法】0-1背包问题

0036算法笔记——【分支限界法】0-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达最大.即一个特殊的整数规划问题。

算法设计首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。

在优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。

算法首先检查当前扩展结点的左儿子结点的可行性。

如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。

当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。

当扩展到叶节点时为问题的最优值。

例如:0-1背包问题,当n=3时,w={16,15,15}, p={45,25,25}, c=30。

优先队列式分支限界法:处理法则:价值大者优先。

{}—>{A}—>{B,C}—>{C,D,E}—>{C,E}—>{C,J,K}—>{C}—>{F,G}—>{G,L,M}—>{G,M}—>{G}—>{N,O}—>{O}—>{}算法代码实现如下:1、MaxHeap.h[cpp]view plain copy1.template<class T>2.class MaxHeap3.{4.public:5. MaxHeap(int MaxHeapSize = 10);6. ~MaxHeap() {delete [] heap;}7.int Size() const {return CurrentSize;}8.9. T Max()10. { //查11.if (CurrentSize == 0)12. {13.throw OutOfBounds();14. }15.return heap[1];16. }17.18. MaxHeap<T>& Insert(const T& x); //增19. MaxHeap<T>& DeleteMax(T& x); //删20.21.void Initialize(T a[], int size, int ArraySize);22.23.private:24.int CurrentSize, MaxSize;25. T *heap; // element array26.};27.28.template<class T>29.MaxHeap<T>::MaxHeap(int MaxHeapSize)30.{// Max heap constructor.31. MaxSize = MaxHeapSize;32. heap = new T[MaxSize+1];33. CurrentSize = 0;34.}35.36.template<class T>37.MaxHeap<T>& MaxHeap<T>::Insert(const T& x)38.{// Insert x into the max heap.39.if (CurrentSize == MaxSize)40. {41. cout<<"no space!"<<endl;42.return *this;43. }44.45.// 寻找新元素x的位置46.// i——初始为新叶节点的位置,逐层向上,寻找最终位置47.int i = ++CurrentSize;48.while (i != 1 && x > heap[i/2])49. {50.// i不是根节点,且其值大于父节点的值,需要继续调整51. heap[i] = heap[i/2]; // 父节点下降52. i /= 2; // 继续向上,搜寻正确位置53. }54.55. heap[i] = x;56.return *this;57.}58.59.template<class T>60.MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)61.{// Set x to max element and delete max element from heap.62.// check if heap is empty63.if (CurrentSize == 0)64. {65. cout<<"Empty heap!"<<endl;66.return *this;67. }68.69. x = heap[1]; // 删除最大元素70.// 重整堆71. T y = heap[CurrentSize--]; // 取最后一个节点,从根开始重整72.73.// find place for y starting at root74.int i = 1, // current node of heap75. ci = 2; // child of i76.77.while (ci <= CurrentSize)78. {79.// 使ci指向i的两个孩子中较大者80.if (ci < CurrentSize && heap[ci] < heap[ci+1])81. {82. ci++;83. }84.// y的值大于等于孩子节点吗?85.if (y >= heap[ci])86. {87.break; // 是,i就是y的正确位置,退出88. }89.90.// 否,需要继续向下,重整堆91. heap[i] = heap[ci]; // 大于父节点的孩子节点上升92. i = ci; // 向下一层,继续搜索正确位置93. ci *= 2;94. }95.96. heap[i] = y;97.return *this;98.}99.100.template<class T>101.void MaxHeap<T>::Initialize(T a[], int size,int ArraySize) 102.{// Initialize max heap to array a.103.delete [] heap;104. heap = a;105. CurrentSize = size;106. MaxSize = ArraySize;107.108.// 从最后一个内部节点开始,一直到根,对每个子树进行堆重整109.for (int i = CurrentSize/2; i >= 1; i--)110. {111. T y = heap[i]; // 子树根节点元素112.// find place to put y113.int c = 2*i; // parent of c is target114.// location for y115.while (c <= CurrentSize)116. {117.// heap[c] should be larger sibling118.if (c < CurrentSize && heap[c] < heap[c+1])119. {120. c++;121. }122.// can we put y in heap[c/2]?123.if (y >= heap[c])124. {125.break; // yes126. }127.128.// no129. heap[c/2] = heap[c]; // move child up130. c *= 2; // move down a level131. }132. heap[c/2] = y;133. }134.}2、6d5.cpp[cpp]view plain copy1.//0-1背包问题分支限界法求解2.#include "stdafx.h"3.#include "MaxHeap.h"4.#include <iostream>ing namespace std;6.7.class Object8.{9.template<class Typew,class Typep>10.friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);11.public:12.int operator <= (Object a) const13. {14.return d>=a.d;15. }16.private:17.int ID;18.float d;//单位重量价值19.};20.21.template<class Typew,class Typep> class Knap;22.23.class bbnode24.{25.friend Knap<int,int>;26.template<class Typew,class Typep>27.friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);28.private:29. bbnode * parent; //指向父节点的指针30.bool LChild; //左儿子节点标识31.};32.33.template<class Typew,class Typep>34.class HeapNode35.{36.friend Knap<Typew,Typep>;37.public:38. operator Typep() const39. {40.return uprofit;41. }42.private:43. Typep uprofit, //节点的价值上界44. profit; //节点所相应的价值45. Typew weight; //节点所相应的重量46.int level; //活节点在子集树中所处的层序号47. bbnode *ptr; //指向活节点在子集中相应节点的指针48.};49.50.template<class Typew,class Typep>51.class Knap52.{53.template<class Typew,class Typep>54.friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);55.public:56. Typep MaxKnapsack();57.private:58. MaxHeap<HeapNode<Typep,Typew>> *H;59. Typep Bound(int i);60.void AddLiveNode(Typep up,Typep cp,Typew cw,bool ch,int lev);61.62. bbnode *E; //指向扩展节点的指针63. Typew c; //背包容量64.int n; //物品数65.66. Typew *w; //物品重量数组67. Typep *p; //物品价值数组68. Typew cw; //当前重量69.70. Typep cp; //当前价值71.int *bestx; //最优解72.};73.74.template <class Type>75.inline void Swap(Type &a,Type &b);76.77.template<class Type>78.void BubbleSort(Type a[],int n);79.80.int main()81.{82.int n = 3;//物品数83.int c = 30;//背包容量84.int p[] = {0,45,25,25};//物品价值下标从1开始85.int w[] = {0,16,15,15};//物品重量下标从1开始86.int bestx[4];//最优解87.88. cout<<"背包容量为:"<<c<<endl;89. cout<<"物品重量和价值分别为:"<<endl;90.91.for(int i=1; i<=n; i++)92. {93. cout<<"("<<w[i]<<","<<p[i]<<") ";94. }95. cout<<endl;96.97. cout<<"背包能装下的最大价值为:"<<Knapsack(p,w,c,n,bestx)<<endl;98. cout<<"此背包问题最优解为:"<<endl;99.for(int i=1; i<=n; i++)100. {101. cout<<bestx[i]<<" ";102. }103. cout<<endl;104.return 0;105.}106.107.template<class Typew,class Typep>108.Typep Knap<Typew,Typep>::Bound(int i)//计算节点所相应价值的上界109.{110. Typew cleft = c-cw; //剩余容量高111. Typep b = cp; //价值上界112.//以物品单位重量价值递减序装填剩余容量113.while(i<=n && w[i]<=cleft)114. {115. cleft -= w[i];116. b += p[i];117. i++;118. }119.120.//装填剩余容量装满背包121.if(i<=n)122. {123. b += p[i]/w[i]*cleft;124. }125.126.return b;127.}128.129.//将一个活节点插入到子集树和优先队列中130.template<class Typew,class Typep>131.void Knap<Typew,Typep>::AddLiveNode(Typep up,Typep cp,Typew cw,bool ch,int lev)132.{133. bbnode *b = new bbnode;134. b->parent = E;135. b->LChild = ch;136.137. HeapNode<Typep,Typew> N;138. N.uprofit = up;139. N.profit = cp;140. N.weight = cw;141. N.level = lev;142. N.ptr = b;143.144. H->Insert(N);145.}146.147.//优先队列式分支限界法,返回最大价值,bestx返回最优解148.template<class Typew,class Typep>149.Typep Knap<Typew,Typep>::MaxKnapsack()150.{151. H = new MaxHeap<HeapNode<Typep,Typew>>(1000);152.153.//为bestx分配存储空间154. bestx = new int[n+1];155.156.//初始化157.int i = 1;158. E = 0;159. cw = cp = 0;160. Typep bestp = 0;//当前最优值161. Typep up = Bound(1); //价值上界162.163.//搜索子集空间树164.while(i!=n+1)165. {166.//检查当前扩展节点的左儿子节点167. Typew wt = cw + w[i];168.if(wt <= c)//左儿子节点为可行节点169. {170.if(cp+p[i]>bestp)171. {172. bestp = cp + p[i];173. }174. AddLiveNode(up,cp+p[i],cw+w[i],true,i+1); 175. }176.177. up = Bound(i+1);178.//检查当前扩展节点的右儿子节点179.if(up>=bestp)//右子树可能含有最优解180. {181. AddLiveNode(up,cp,cw,false,i+1);182. }183.184.//取下一扩展节点185. HeapNode<Typep,Typew> N;186. H->DeleteMax(N);187. E = N.ptr;188. cw = N.weight;189. cp = N.profit;190. up = N.uprofit;191. i = N.level;192. }193.194.//构造当前最优解195.for(int j=n; j>0; j--)196. {197. bestx[j] = E->LChild;198. E = E->parent;199. }200.return cp;201.}202.203.//返回最大价值,bestx返回最优解204.template<class Typew,class Typep>205.Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]) 206.{207.//初始化208. Typew W = 0; //装包物品重量209. Typep P = 0; //装包物品价值210.211.//定义依单位重量价值排序的物品数组212. Object *Q = new Object[n];213.for(int i=1; i<=n; i++)214. {215.//单位重量价值数组216. Q[i-1].ID = i;217. Q[i-1].d = 1.0*p[i]/w[i];218. P += p[i];219. W += w[i];220. }221.222.if(W<=c)223. {224.return P;//所有物品装包225. }226.227.//依单位价值重量排序228. BubbleSort(Q,n);229.230.//创建类Knap的数据成员231. Knap<Typew,Typep> K;232. K.p = new Typep[n+1];233. K.w = new Typew[n+1];234.235.for(int i=1; i<=n; i++)236. {237. K.p[i] = p[Q[i-1].ID];238. K.w[i] = w[Q[i-1].ID];239. }240.241. K.cp = 0;242. K.cw = 0;243. K.c = c;244. K.n = n;245.246.//调用MaxKnapsack求问题的最优解247. Typep bestp = K.MaxKnapsack();248.for(int j=1; j<=n; j++)249. {250. bestx[Q[j-1].ID] = K.bestx[j]; 251. }252.253.delete Q;254.delete []K.w;255.delete []K.p;256.delete []K.bestx;257.return bestp;258.}259.260.template<class Type>261.void BubbleSort(Type a[],int n)262.{263.//记录一次遍历中是否有元素的交换264.bool exchange;265.for(int i=0; i<n-1;i++)266. {267. exchange = false ;268.for(int j=i+1; j<=n-1; j++) 269. {270.if(a[j]<=a[j-1])271. {272. Swap(a[j],a[j-1]);273. exchange = true;274. }275. }276.//如果这次遍历没有元素的交换,那么排序结束277.if(false == exchange)278. {279.break ;280. }281. }282.}283.284.template <class Type>285.inline void Swap(Type &a,Type &b) 286.{287. Type temp = a;288. a = b;289. b = temp;290.}程序运行结果如图:。

最新实验 4 用分支限界法实现0-1背包问题

最新实验 4 用分支限界法实现0-1背包问题

实验四用分支限界法实现0-1背包问题一.实验目的1.熟悉分支限界法的基本原理。

2.通过本次实验加深对分支限界法的理解。

二.实验内容及要求内容:.给定n种物品和一个背包。

物品i的重量是w,其价值为v,背包容量为c。

问应该如何选择装入背包的物品,使得装入背包中物品的总价值最大?要求:使用优先队列式分支限界法算法编程,求解0-1背包问题三.程序列表#include<iostream>#include<stack>using namespace std;#define N 100class HeapNode//定义HeapNode结点类{public:double upper, price, weight; //upper为结点的价值上界,price是结点所对应的价值,weight 为结点所相应的重量int level, x[N]; //活节点在子集树中所处的层序号};double MaxBound(int i);double Knap();void AddLiveNode(double up, double cp, double cw, bool ch, int level);//up是价值上界,cp是相应的价值,cw是该结点所相应的重量,ch是ture or falsestack<HeapNode> High; //最大队Highdouble w[N], p[N]; //把物品重量和价值定义为双精度浮点数double cw, cp, c; //cw为当前重量,cp为当前价值,定义背包容量为cint n; //货物数量为int main(){cout <<"请输入背包容量:"<< endl;cin >> c;cout <<"请输入物品的个数:"<< endl;cin >> n;cout <<"请按顺序分别输入物品的重量:"<< endl;int i;for (i = 1; i <= n; i++)cin >> w[i]; //输入物品的重量cout <<"请按顺序分别输入物品的价值:"<< endl;for (i = 1; i <= n; i++)cin >> p[i]; //输入物品的价值cout <<"最优值为:";cout << Knap() << endl; //调用knap函数输出最大价值return 0;}double MaxBound(int k) //MaxBound函数求最大上界{double cleft = c - cw; //剩余容量double b = cp; //价值上界while (k <= n&&w[k] <= cleft) //以物品单位重量价值递减装填剩余容量{cleft -= w[k];b += p[k];k++;}if (k <= n)b += p[k] / w[k] * cleft; //装填剩余容量装满背包return b;}void AddLiveNode(double up, double cp, double cw, bool ch, int lev) //将一个新的活结点插入到子集数和最大堆High中{HeapNode be;be.upper = up;be.price = cp;be.weight = cw;be.level = lev;if (lev <= n)High.push(be);}//调用stack头文件的push函数 }double Knap() //优先队列分支限界法,返回最大价值,bestx返回最优解{int i = 1;cw = cp = 0;double bestp = 0; //best为当前最优值double up = MaxBound(1);//价值上界//搜索子集空间树while (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], true, i + 1);}up = MaxBound(i + 1);if (up >= bestp) //右子数可能含最优解AddLiveNode(up, cp, cw, false, i + 1);if (High.empty())return bestp;HeapNode node = High.top(); //取下一扩展结点High.pop();cw = node.weight;cp = node.price;up = node.upper;i = node.level;}}四.实验结果酒店服务员年度工作汇报20xx年是自我挑战的一年,我将努力改正过去一年工作中的不足,把新一年的工作做好,过去的一年在领导的关心和同事的热情帮助,通过自身的不懈努力,在工作上取得了一定的成果,现将工作总结如下。

分支限界法解决01背包问题

分支限界法解决01背包问题

分⽀限界法解决01背包问题 分⽀限界法和之前讲的回溯法有⼀点相似,两者都是在问题的解的空间上搜索问题的解。

但是两者还是有⼀些区别的,回溯法是求解在解的空间中的满⾜的所有解,分⽀限界法则是求解⼀个最⼤解或最⼩解。

这样,两者在解这⼀⽅⾯还是有⼀些不同的。

之前回溯法讲了N后问题,这个问题也是对于这有多个解,但是今天讲的01背包问题是只有⼀个解的。

下⾯就讲讲分⽀限界法的基本思想。

分⽀限界法常以⼴度优先或以最⼩消耗(最⼤效益)优先的⽅式搜索问题的解空间树。

问题的解空间树是表⽰问题解空间的⼀颗有序树,常见的有⼦集树和排列树。

分⽀限界法和回溯法的区别还有⼀点,它们对于当前扩展结点所采⽤的扩展⽅式也是不相同的。

分⽀限界法中,对于每⼀个活结点只有⼀次机会成为扩展结点。

活结点⼀旦成为了扩展结点,就⼀次性产⽣其所有的⼦结点,⼦结点中,不符合要求的和⾮最优解的⼦结点将会被舍弃,剩下的⼦结点将加⼊到活结点表中。

再重复上⾯的过程,直到没有活结点表中没有结点,⾄此完成解决问题的⽬的。

分⽀限界法⼤致的思想就是上⾯的叙述,现在就可以发现,对于结点的扩展将会成为分⽀限界法的主要核⼼。

所以,分⽀限界法常见的有两种扩展结点的⽅式,1.队列式(FIFO)分⽀限界法,2.优先队列式分⽀限界法。

两种⽅法的区别就是对于活结点表中的取出结点的⽅式不同,第⼀种⽅法是先进先出的⽅式,第⼆种是按优先级取出结点的⽅式。

两中⽅法的区别下⾯也会提到。

在背包问题中还会提到⼀个⼦树上界的概念,其实就是回溯法中的剪枝函数,只不过,分⽀限界法⾥的剪枝函数改进了⼀些,剪枝函数同样也是分⽀限界法⾥⽐较重要的东西。

下⾯就讲⼀讲01背包问题的实现。

01背包问题和前⾯讲的背包问题的区别不⼤,就是01背包问题的物品不可以只放⼊部分,01背包问题的物品只能放⼊和不放⼊两个选择,这也是名字中01的原因。

其他的和背包问题相差不⼤,这⾥也不再累述。

算法的主体是⽐较容易想的,⾸先,将数据进⾏处理,这也是上⾯讲到的第⼆种取结点的⽅式(优先队列式)。

分支限界法0-1背包问题-队列式

分支限界法0-1背包问题-队列式

分⽀限界法0-1背包问题-队列式⼀.分⽀限界法概述(1)分⽀限界法就是采⽤⼴度优先的策略,依次搜索活结点所有的分枝,也就额是所有的相邻结点。

在求最优解时采⽤⼀个限界函数,计算限界函数值,选择⼀个最有利的⼦节点作为扩展结点,使搜索树朝着解空间树上有最优解的分⽀推进,以便尽快找出⼀个最优解。

(2)常见的两种分⽀限界法 先进先出(FIFO)队列式:在先进先出的分⽀限界法中,⽤队列作为组织活结点表的数据结构,并按照队列先进先出的原则选择结点作为扩展结点。

优先队列(PQ):⽤优先队列作为组织活结点表的数据结构。

⼆.0-1背包问题问题:给定n种物品和⼀背包。

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

问应如何选择装⼊背包的物品,使得装⼊背包中物品的总价值最⼤?#include<iostream>#include<queue>using namespace std;const int maxn=99;int n,c;int w[maxn];int v[maxn];int bestv=0;int bestx[maxn];int total=1; //解空间中的节点数累计,全局变量struct nodetype //队列中的结点类型{int no; //结点编号,从1开始int i; //当前结点在搜索空间中的层次int w; //当前结点的总重量int v; //当前结点的总价值int x[maxn]; //当前结点包含的解向量double ub; //上界};void input(){cout<<"请输⼊物品的个数:"<<endl;cin>>n;cout<<"请输⼊每个物品的重量及价值(如5 4):"<<endl;for(int i = 1; i <= n; i++){cin>>w[i]>>v[i];}cout<<"请输⼊背包的容量:"<<endl;cin>>c;}void bound(nodetype &e) //计算分⽀结点e的上界{int i=e.i+1; //考虑结点e的余下物品int sumw=e.w;double sumv=e.v;while((sumw+w[i]<=c)&&i<=n){sumw+=w[i];sumv+=v[i];i++;}if(i<=n) //余下物品只能部分装⼊e.ub=sumv+(c-sumw)*v[i]/w[i];else e.ub=sumv;}void enqueue(nodetype e,queue<nodetype> &qu)//结点e进队qu{if(e.i==n) //到达叶⼦节点,不在扩展对应⼀个解{if(e.v>bestv) //找到更⼤价值的解{bestv=e.v;for(int j=1;j<=n;j++)bestx[j]=e.x[j];}}else qu.push(e); //⾮叶⼦结点进队}void bfs(){int j;nodetype e,e1,e2;queue<nodetype> qu;e.i=0;e.w=0;e.v=0;e.no=total++;for(j=1;j<=n;j++)e.x[j]=0;bound(e);qu.push(e);while(!qu.empty()){e=qu.front();qu.pop(); //出队结点eif(e.w+w[e.i+1]<=c) //剪枝,检查左孩⼦结点{e1.no=total++; //建⽴左孩⼦结点e1.i=e.i+1;e1.w=e.w+w[e1.i];e1.v=e.v+v[e1.i];for(j=1;j<=n;j++)e1.x[j]=e.x[j];e1.x[e1.i]=1;bound(e1); //求左孩⼦的上界enqueue(e1,qu); //左孩⼦结点进队}e2.no=total++;e2.i=e.i+1;e2.w=e.w;e2.v=e.v;for(j=1;j<=n;j++)e2.x[j]=e.x[j];e2.x[e2.i]=0;bound(e2);if(e2.ub>bestv) //若右孩⼦结点可⾏,则进队,否则被剪枝 enqueue(e2,qu);}}void output(){cout<<"最优值是:"<<bestv<<endl;cout<<"(";for(int i=1;i<=n;i++)cout<<bestx[i]<<"";cout<<")";}int main(){input();bfs();output();return0;}。

用分支限界求解0-1背包问题

用分支限界求解0-1背包问题

实验题目:用分支限界求解0-1背包问题物品个数n=4,背包容量c=7,价值向量p={9,10,7,4},重量向量w={3,5,2,1}请求出最优的解及其目标函数值。

#include <stdio.h>#include<malloc.h>#defineMaxSize 100//最多结点数typedefstructQNode{floatweight;floatvalue;intceng;structQNode *parent;bool leftChild;}QNode,*qnode;//存放每个结点typedef struct{qnodeQ[MaxSize];int front,rear;}SqQueue;//存放结点的队列SqQueuesq;float bestv=0; //最优解int n=0;//实际物品数float w[MaxSize];//物品的重量float v[MaxSize];//物品的价值int bestx[MaxSize];//存放最优解qnode bestE;void InitQueue(SqQueue &sq ) //队列初始化{}sq.front=1;sq.rear=1;bool QueueEmpty(SqQueue sq) //队列是否为空{}void EnQueue(SqQueue &sq,qnode b)//入队{}qnode DeQueue(SqQueue &sq)//出队{qnode e;if(sq.front==sq.rear){}e=sq.Q[sq.front];printf("队列已空!");return 0;if(sq.front==(sq.rear+1)%MaxSize){}sq.Q[sq.rear]=b;sq.rear=(sq.rear+1)%MaxSize;printf("队列已满!");return ;if(sq.front==sq.rear)return true;elsereturn false;}sq.front=(sq.front+1)%MaxSize;return e;voidEnQueue1(float wt,float vt, int i ,QNode *parent, bool leftchild) {}voidmaxLoading(float w[],float v[],int c){float wt=0;float vt=0;int i=1;//当前的扩展结点所在的层qnode b;if (i==n)//可行叶子结点{}b=(qnode)malloc(sizeof(QNode));//非叶子结点b->weight=wt;b->value=vt;b->ceng=i;b->parent=parent;b->leftChild=leftchild;EnQueue(sq,b);if (vt==bestv){}return;bestE=parent;bestx[n]=(leftchild)?1:0;float ew=0;//扩展节点所相应的当前载重量float ev=0;//扩展结点所相应的价值qnode e=NULL;qnode t=NULL;InitQueue(sq);EnQueue(sq,t);//空标志进队列while (!QueueEmpty(sq)){}printf("最优取法为:\n");wt=ew+w[i];vt=ev+v[i];if (wt <= c){if(vt>bestv)bestv=vt;EnQueue1(wt,vt,i,e,true);//左儿子结点进队列}EnQueue1(ew,ev,i,e,false);//右儿子总是可行;e=DeQueue(sq);//取下一扩展结点if (e == NULL){}ew=e->weight;//更新当前扩展结点的值ev=e->value;if (QueueEmpty(sq))break; EnQueue(sq,NULL);//同层结点尾部标志e=DeQueue(sq);//取下一扩展结点i++;for( int j=n-1;j>0;j--)//构造最优解{}for(int k=1;k<=n;k++){}printf("\n");printf("最优价值为:%.1f\n\n",bestv);if(bestx[k]==1)printf("\n物品%d:重量:%.1f,价值:%.1f\n",k,w[k],v[k]);bestx[j]=(bestE->leftChild?1:0);bestE=bestE->parent;}void main(){int c;float ewv[MaxSize];printf("510专区\n");printf("请输入物品的数量:\n");scanf("%d",&n);printf("请输入背包容量:\n");scanf("%d",&c);printf("\n请输入物品价值和重量:\n\n"); for(int i=1;i<=n;i++){printf("物品%d:",i);scanf("%f%f",&ewv[i],&w[i]);v[i]=w[i]*ewv[i];printf("\n");}}实验结果:maxLoading(w, v, c);。

分支限界策略求解01背包问题

分支限界策略求解01背包问题

分支限界策略求解0/1背包问题
1
算法思想
2
算法描述(步骤)
3
算法实现
4
程序测试
算法思想
❖ 要想找到01背包问题的最佳选择,本质上还得穷举,而采用 的分支限界就是把穷举的部分选择筛选掉。
❖ 背包对于每个物品只有两种状态,装或者不装,即1和0。我 们对每个物品选择做一个记录,则为 路径。假设目前取得 的一种路径已经带来的价值就比其他路径的可能最大值还要 高,那么自然这些其他路径就不在我们的考虑范围内了。
分支限界策略求解0/1背包问题
❖0/1背包问题
有N件物品和一个容量为V的背包。第i件物品的体积 是c[i],价值是w[i]。求解将哪些物品装入背包可使价 值总和最大。在01背包问题中,因为每种物品只有一个 ,对于每个物品只需要考虑选与不选两种情况。如果不 选择将其放入背包中,则不需要处理,如果选择将其放 入背包中,需要枚举将这个物品放入背包后可能占据背 包空间的所有情况。
❖ 而如果我们更早选到一种高价值的路径,就能筛选掉更多的 低价值路径。那么我们如何尽早取到高价值路劲呢?我们可 以给每个物品一个估计价值,性价比。
❖ 然后通过性价比计算剩余重量,我们就能估计到该路径下可 能取得的最大值,及该路径上界。同时通过 优先取性价比 较高的物品 和 优先搜索上界高的路径 就能更容易取到一 条高价值路径。最终遍历各个筛选下来的路径就能找到最优 解。
当输入数据为60组时,运行时间4秒左 右。
当输入数据为70组时,运行时间42秒 左右。
程序测试
当数据更大时,程序的效率就很慢了。
当数据为75组时,运行时间18分钟左右。
当数据为80组时,运行时间1小时左右。
因此,该程序的效率不高,但是准确性很 高,以上4组数据的测试结果和最优解是一 致的。

分枝定界法解01背包问题

分枝定界法解01背包问题

分枝定界法解01背包问题分枝定界法解0/1背包问题关键词:分⽀定界、0-1背包分枝定界法简介分枝定界法按照树形结构来组织解空间,展开节点后,有两种策略:策略⼀、把节点加⼊ FIFO 队列当中;策略⼆、把节点加⼊到堆中,按照最⼩消耗或者最⼤消耗来选择下⼀个展开节点。

感悟分枝定界法是⼀类能确保搜索到全部可⾏解的解空间的⼀类⽅法。

得到全部的解以后,再由后续程序⾃⾏觉得。

分枝定界法与回溯法的区别分枝定界法回溯法搜索解空间的⽅法是 BFS(⼴度优先)搜索解空间的⽅法是 DFS(深度优先)算法搜索的解空间⼤,消耗的内存多算法搜索的解空间⼤,消耗的内存少0/1背包问题有背包问题形式化描述如下:\[n = 3 \\ weights = [20, 15, 15] \\ values = [40, 25, 25] \\ constraint = 30 \]⽤分枝定界法,代码实现如下:# -*- coding:utf8 -*-import copyfrom collections import deque# ⽤分枝定界法解决0/1背包问题def brunch_bound(w, p, c):plan = list()buffer = deque([[copy.copy(w), [], []]]) # [waiting, explored, values]while len(buffer):cur = buffer.popleft()for i in range(0, len(cur[0])):weight = cur[0][i]n = copy.deepcopy(cur)n[0].remove(weight)n[1].append(weight)n[2].append(p[i])if sum(n[1]) > c:continue # overloadedbuffer.append(copy.deepcopy(n))plan.append(cur)return planif __name__ == '__main__':w = [20, 15, 15]p = [40, 25, 25]c = 30plan = brunch_bound(w,p,c)l = sorted(plan, key=lambda x:sum(x[2]), reverse=True) # l[0] is the most value planfor e in l:print(e)输出:[[20], [15, 15], [25, 25]] #显然,这是最优⽅案[[20], [15, 15], [25, 25]][[15, 15], [20], [40]][[20, 15], [15], [25]][[20, 15], [15], [25]][[20, 15, 15], [], []]解释copy.copy 是浅拷贝,⼦对象不会被拷贝copy.deepcopy 是深拷贝。

【算法问题】0-1背包问题

【算法问题】0-1背包问题

【算法问题】0-1背包问题 0-1背包问题:有⼀个贼在偷窃⼀家商店时,发现有n件物品,第i件物品价值v i元,重w i磅,此处v i与w i都是整数。

他希望带⾛的东西越值钱越好,但他的背包中⾄多只能装下W磅的东西,W为⼀整数。

应该带⾛哪⼏样东西?这个问题之所以称为0-1背包,是因为每件物品或被带⾛;或被留下;⼩偷不能只带⾛某个物品的⼀部分或带⾛同⼀物品两次。

在分数(部分)背包问题(fractional knapsack problem)中,场景与上⾯问题⼀样,但是窃贼可以带⾛物品的⼀部分,⽽不必做出0-1的⼆分选择。

可以把0-1背包问题的⼀件物品想象成⼀个⾦锭,⽽部分问题中的⼀件物品则更像⾦沙。

两种背包问题都具有最优⼦结构性质。

对0-1背包问题,考虑重量不超过W⽽价值最⾼的装包⽅案。

如果我们将商品j从此⽅案中删除,则剩余商品必须是重量不超过W-wj的价值最⾼的⽅案(⼩偷只能从不包括商品j的n-1个商品中选择拿⾛哪些)。

虽然两个问题相似,但我们⽤贪⼼策略可以求解背包问题,⽽不能求解0-1背包问题,为了求解部分数背包问题,我们⾸先计算每个商品的每磅价值v i/w i。

遵循贪⼼策略,⼩偷⾸先尽量多地拿⾛每磅价值最⾼的商品,如果该商品已全部拿⾛⽽背包未装满,他继续尽量多地拿⾛每磅价值第⼆⾼的商品,依次类推,直到达到重量上限W。

因此,通过将商品按每磅价值排序,贪⼼算法的时间运⾏时间是O(nlgn)。

为了说明贪⼼这⼀贪⼼策略对0-1背包问题⽆效,考虑下图所⽰的问题实例。

此例包含3个商品和⼀个能容纳50磅重量的背包。

商品1重10磅,价值60美元。

商品2重20磅,价值100美元。

商品3重30磅,价值120美元。

因此,商品1的每磅价值为6美元,⾼于商品2的每磅价值5美元和商品3的每磅价值4美元。

因此,上述贪⼼策略会⾸先拿⾛商品1。

但是,最优解应该是商品2和商品3,⽽留下商品1。

拿⾛商品1的两种⽅案都是次优的。

分支限界法 01背包问题c语言

分支限界法 01背包问题c语言

分支限界法 01背包问题c语言分支限界法是一种解决组合优化问题的算法。

其中,01背包问题是一种经典的背包问题,它要求在给定的容量下,选择商品的组合,使得组合的总价值达到最大化,但组合中每种商品只能选择一次。

C语言是一种广泛使用的编程语言,适用于实现各种算法和数据结构。

下面我将用C语言实现分支限界法来解决01背包问题。

首先,我们定义一个结构体用来表示商品的信息,包括商品的重量和价值:```typedef struct {int weight;int value;} Item;```接下来,我们定义一个递归函数来实现分支限界法。

该函数通过深度优先搜索的方式,尝试不同的选择,并计算当前组合的总价值。

如果当前组合的总价值已经超过了已知的最优解,则剪枝,不再继续搜索。

```void branchAndBound(int index, int capacity, int currentWeight, int currentValue, int n, Item items[], int bestValue, int choice[]) {if (index >= n || currentWeight >= capacity) {if (currentValue > bestValue) {bestValue = currentValue;// 更新最优解for (int i = 0; i < n; i++) {choice[i] = tempChoice[i];}}return;}// 选择当前商品if (currentWeight + items[index].weight <= capacity) {currentWeight += items[index].weight;currentValue += items[index].value;tempChoice[index] = 1;branchAndBound(index + 1, capacity, currentWeight, currentValue, n, items, bestValue, choice);currentWeight -= items[index].weight;currentValue -= items[index].value;tempChoice[index] = 0;}// 不选择当前商品branchAndBound(index + 1, capacity, currentWeight, currentValue, n, items, bestValue, choice);}最后,我们可以在主函数中调用分支限界法来解决01背包问题。

《程序设计创新》分支限界法解决01背包问题

《程序设计创新》分支限界法解决01背包问题

《程序设计创新》分支限界法解决01背包问题一、引言分枝限界法通常以广度优先或最小成本(最大收益)优先搜索问题的解空间树。

在分枝限界方法中,每个活动节点只有一次成为扩展节点的机会。

当活动节点成为扩展节点时,将同时生成所有子节点。

这些子节点将丢弃不可执行或非最优解的子节点,并将剩余的子节点添加到活动节点表中。

然后,从活动节点表中选择节点作为当前扩展节点,然后重复上述节点扩展过程。

此过程将持续到所需的解决方案或节点表为空。

二、研究背景在生活或企业活动中,我们常常会遇到一些装在问题。

例如在生活中我们要出去旅游,背包的容量是有限的而要装物品可能很多,但是每个物品的装载优先级肯定是不一样的,那么怎么装更合适一些呢。

在企业活动中,比如轮船集装箱装载问题,集装箱是有限的,那么怎么装载这些货物才能每次都是装载最多的,只有这样企业利润才能最大化。

三、相关技术介绍上述问题就是我们算法中会遇到的背包问题。

而背包问题又分许多。

如背包问题,通常用贪心法解决。

如01背包问题通常用动态规划或者分支限界法解决。

本次我们考虑使用分支限界法来解决01背包问题四、应用示例在01背包问题中,假设有四个物品。

重量W(4,7,5,3),价值V(40,42,25,12),背包重量W为10,试求出最佳装载方案。

定义限界函数: ub = v + (W-w)×(Vi+1/W+1)画出状态空间树的搜索图步骤:①在根结点1,没有将任何物品装入背包,因此,背包的重量和获得的价值均为0,根据限界函数计算结点1的目标函数值为10×10=100;②在结点2,将物品1装入背包,因此,背包的重量为4,获得的价值为40,目标函数值为40 + (10-4)×6=76,将结点2加入待处理结点表PT中;在结点3,没有将物品1装入背包,因此,背包的重量和获得的价值仍为0,目标函数值为10×6=60,将结点3加入表PT 中;③在表PT中选取目标函数值取得极大的结点2优先进行搜索;④在结点4,将物品2装入背包,因此,背包的重量为11,不满足约束条件,将结点4丢弃;在结点5,没有将物品2装入背包,因此,背包的重量和获得的价值与结点2相同,目标函数值为40 + (10-4)×5=70,将结点5加入表PT中;⑤在表PT中选取目标函数值取得极大的结点5优先进行搜索;⑥在结点6,将物品3装入背包,因此,背包的重量为9,获得的价值为65,目标函数值为65 + (10-9)×4=69,将结点6加入表PT中;在结点7,没有将物品3装入背包,因此,背包的重量和获得的价值与结点5相同,目标函数值为40 + (10-4)×4=64,将结点6加入表PT中;⑦在表PT中选取目标函数值取得极大的结点6优先进行搜索;⑧在结点8,将物品4装入背包,因此,背包的重量为12,不满足约束条件,将结点8丢弃;在结点9,没有将物品4装入背包,因此,背包的重量和获得的价值与结点6相同,目标函数值为65;⑨由于结点9是叶子结点,同时结点9的目标函数值是表PT中的极大值,所以,结点9对应的解即是问题的最优解,搜索结束。

0-1背包的分支限界算法

0-1背包的分支限界算法

0-1背包的分支限界算法
0-1背包问题是一个经典的优化问题,其分支限界算法的基本步骤如下:
1.创建一个活结点表,将根节点放入活结点表。

2.设定一个函数值(限界),用于在每一活结点处计算,并根据这些已计算出的函数值从当前活结点表中选择一个最有利的结点作为扩展,使搜索朝着解空间树上最优解的分支推进,以便尽快找出一个最优解。

3.在扩展节点处,首先生成其所有的儿子结点(分支)。

4.判断这些儿子结点是否导致不可行解或非最优解,如果是,则舍弃该节点,否则将该节点加入活结点表。

5.从活结点表取出下一结点成为当前扩展结点,重复上述扩展过程,直到找到最优解或活结点表为空时停止。

以上是0-1背包问题的分支限界算法的基本步骤,实际操作中可能需要根据具体问题进行一些调整。

分支限界法-01背包问题

分支限界法-01背包问题

分⽀限界法-01背包问题1、分⽀限界法介绍分⽀限界法类似于,也是在问题的解空间上搜索问题解的算法。

⼀般情况下,分⽀限界法与回溯法的求解⽬标不同。

回溯法的求解⽬标是找出解空间中满⾜约束条件的所有解;⽽分⽀限界法的求解⽬标则是找出满⾜约束条件的⼀个解,或是在满⾜约束条件的解中找出使某⼀⽬标函数值达到极⼤或极⼩的解,即在某种意义下的最优解。

由于求解⽬标不同,导致分⽀限界法与回溯法对解空间的搜索⽅式也不相同。

回溯法以深度优先的⽅式搜索解空间,⽽分⽀限界法则以⼴度优先或以最⼩耗费优先的⽅式搜索解空间。

分⽀限界法的搜索策略是,在扩展结点处,先⽣成其所有的⼉⼦结点(分⽀),然后再从当前的活结点表中选择下⼀扩展结点。

为了有效地选择下⼀扩展结点,加速搜索的进程,在每⼀个活结点处,计算⼀个函数值(限界),并根据函数值,从当前活结点表中选择⼀个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分⽀推进,以便尽快地找出⼀个最优解。

这种⽅式称为分⽀限界法。

⼈们已经⽤分⽀限界法解决了⼤量离散最优化的问题。

2、常见的两种分⽀限界法1. 队列式(FIFO)分⽀限界法:按照先进先出原则选取下⼀个节点为扩展节点。

活结点表是先进先出队列。

LIFO分⽀限界法:活结点表是堆栈。

2. LC(least cost)分⽀限界法(优先队列式分⽀限界法):按照优先队列中规定的优先级选取优先级最⾼的节点成为当前扩展节点。

活结点表是优先权队列,LC分⽀限界法将选取具有最⾼优先级的活结点出队列,成为新的E-结点。

FIFO分⽀限界法搜索策略:§⼀开始,根结点是唯⼀的活结点,根结点⼊队。

§从活结点队中取出根结点后,作为当前扩展结点。

§对当前扩展结点,先从左到右地产⽣它的所有⼉⼦,⽤约束条件检查,把所有满⾜约束函数的⼉⼦加⼊活结点队列中。

§再从活结点表中取出队⾸结点(队中最先进来的结点)为当前扩展结点,……,直到找到⼀个解或活结点队列为空为⽌。

分支界线法01背包问题c语言

分支界线法01背包问题c语言

分支界线法01背包问题c语言一、问题描述01背包问题是常见的动态规划问题,其描述如下:有一个背包,最多能承载重量为W的物品。

现在有n个物品,其重量分别为w1, w2, ..., wn,价值分别为v1, v2, ..., vn。

要求选取若干物品放入背包,使得放入背包的物品总价值最大,且总重量不超过W。

二、分支界线法思想分支界线法是一种求解组合优化问题的常用方法。

在01背包问题中,分支界线法的思想是通过一个优先级队列,不断生成和扩展状态空间树,记录每个节点的上界评价函数值,并根据上界值进行搜索剪枝,直至获得最优解。

三、算法步骤1. 定义物品结构体```ctypedef struct {double value; // 物品价值double weight; // 物品重量double unitValue; // 物品单位价值} Item;```2. 比较函数定义(用于优先级队列)```cintpare(const void* a, const void* b) {Item* itemA = (Item*)a;Item* itemB = (Item*)b;double diff = itemB->unitValue - itemA->unitValue; return diff < 0 ? -1 : diff > 0 ? 1 : 0;}```3. 分支界线法求解01背包问题```cdouble knapsack(int n, double W, Item* items) {qsort(items, n, sizeof(Item),pare);double maxValue = 0;double currentWeight = 0;for (int i = 0; i < n; i++) {if (currentWeight + items[i].weight <= W) {currentWeight += items[i].weight;maxValue += items[i].value;} else {double rem本人nWeight = W - currentWeight;maxValue += items[i].unitValue * rem本人nWeight;break;}}return maxValue;}```四、代码实现解释1. 首先根据物品单位价值对物品进行排序,通过单位价值可以快速确定选择哪个物品放入背包;2. 依次选择单位价值最高的物品放入背包,若背包容量不足则按部分放入;3. 根据剩余容量,估算能够放入的最大价值。

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

问题描述给定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达最大.即一个特殊的整数规划问题。

算法设计首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。

在优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。

算法首先检查当前扩展结点的左儿子结点的可行性。

如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。

当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。

当扩展到叶节点时为问题的最优值。

例如:0-1背包问题,当n=3时,w={16,15,15}, p={45,25,25}, c=30。

优先队列式分支限界法:处理法则:价值大者优先。

{}—>{A}—>{B,C}—>{C,D,E}—>{C,E}—>{C,J,K}—>{C}—>{F,G}—>{G,L,M}—>{G,M}—>{G}—>{N,O}—>{O}—>{}算法代码实现如下:1、MaxHeap.h[cpp]view plain copy1.template<class T>2.class MaxHeap3.{4.public:5. MaxHeap(int MaxHeapSize = 10);6. ~MaxHeap() {delete [] heap;}7.int Size() const {return CurrentSize;}8.9. T Max()10. { //查11.if (CurrentSize == 0)12. {13.throw OutOfBounds();14. }15.return heap[1];16. }17.18. MaxHeap<T>& Insert(const T& x); //增19. MaxHeap<T>& DeleteMax(T& x); //删20.21.void Initialize(T a[], int size, int ArraySize);22.23.private:24.int CurrentSize, MaxSize;25. T *heap; // element array26.};27.28.template<class T>29.MaxHeap<T>::MaxHeap(int MaxHeapSize)30.{// Max heap constructor.31. MaxSize = MaxHeapSize;32. heap = new T[MaxSize+1];33. CurrentSize = 0;34.}35.36.template<class T>37.MaxHeap<T>& MaxHeap<T>::Insert(const T& x)38.{// Insert x into the max heap.39.if (CurrentSize == MaxSize)40. {41. cout<<"no space!"<<endl;42.return *this;43. }44.45.// 寻找新元素x的位置46.// i——初始为新叶节点的位置,逐层向上,寻找最终位置47.int i = ++CurrentSize;48.while (i != 1 && x > heap[i/2])49. {50.// i不是根节点,且其值大于父节点的值,需要继续调整51. heap[i] = heap[i/2]; // 父节点下降52. i /= 2; // 继续向上,搜寻正确位置53. }54.55. heap[i] = x;56.return *this;57.}58.59.template<class T>60.MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)61.{// Set x to max element and delete max element from heap.62.// check if heap is empty63.if (CurrentSize == 0)64. {65. cout<<"Empty heap!"<<endl;66.return *this;67. }68.69. x = heap[1]; // 删除最大元素70.// 重整堆71. T y = heap[CurrentSize--]; // 取最后一个节点,从根开始重整72.73.// find place for y starting at root74.int i = 1, // current node of heap75. ci = 2; // child of i76.77.while (ci <= CurrentSize)78. {79.// 使ci指向i的两个孩子中较大者80.if (ci < CurrentSize && heap[ci] < heap[ci+1])81. {82. ci++;83. }84.// y的值大于等于孩子节点吗?85.if (y >= heap[ci])86. {87.break; // 是,i就是y的正确位置,退出88. }89.90.// 否,需要继续向下,重整堆91. heap[i] = heap[ci]; // 大于父节点的孩子节点上升92. i = ci; // 向下一层,继续搜索正确位置93. ci *= 2;94. }95.96. heap[i] = y;97.return *this;98.}99.100.template<class T>101.void MaxHeap<T>::Initialize(T a[], int size,int ArraySize) 102.{// Initialize max heap to array a.103.delete [] heap;104. heap = a;105. CurrentSize = size;106. MaxSize = ArraySize;107.108.// 从最后一个内部节点开始,一直到根,对每个子树进行堆重整109.for (int i = CurrentSize/2; i >= 1; i--)110. {111. T y = heap[i]; // 子树根节点元素112.// find place to put y113.int c = 2*i; // parent of c is target114.// location for y115.while (c <= CurrentSize)116. {117.// heap[c] should be larger sibling118.if (c < CurrentSize && heap[c] < heap[c+1])119. {120. c++;121. }122.// can we put y in heap[c/2]?123.if (y >= heap[c])124. {125.break; // yes126. }127.128.// no129. heap[c/2] = heap[c]; // move child up130. c *= 2; // move down a level131. }132. heap[c/2] = y;133. }134.}2、6d5.cpp[cpp]view plain copy1.//0-1背包问题分支限界法求解2.#include "stdafx.h"3.#include "MaxHeap.h"4.#include <iostream>ing namespace std;6.7.class Object8.{9.template<class Typew,class Typep>10.friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);11.public:12.int operator <= (Object a) const13. {14.return d>=a.d;15. }16.private:17.int ID;18.float d;//单位重量价值19.};20.21.template<class Typew,class Typep> class Knap;22.23.class bbnode24.{25.friend Knap<int,int>;26.template<class Typew,class Typep>27.friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);28.private:29. bbnode * parent; //指向父节点的指针30.bool LChild; //左儿子节点标识31.};32.33.template<class Typew,class Typep>34.class HeapNode35.{36.friend Knap<Typew,Typep>;37.public:38. operator Typep() const39. {40.return uprofit;41. }42.private:43. Typep uprofit, //节点的价值上界44. profit; //节点所相应的价值45. Typew weight; //节点所相应的重量46.int level; //活节点在子集树中所处的层序号47. bbnode *ptr; //指向活节点在子集中相应节点的指针48.};49.50.template<class Typew,class Typep>51.class Knap52.{53.template<class Typew,class Typep>54.friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);55.public:56. Typep MaxKnapsack();57.private:58. MaxHeap<HeapNode<Typep,Typew>> *H;59. Typep Bound(int i);60.void AddLiveNode(Typep up,Typep cp,Typew cw,bool ch,int lev);61.62. bbnode *E; //指向扩展节点的指针63. Typew c; //背包容量64.int n; //物品数65.66. Typew *w; //物品重量数组67. Typep *p; //物品价值数组68. Typew cw; //当前重量69.70. Typep cp; //当前价值71.int *bestx; //最优解72.};73.74.template <class Type>75.inline void Swap(Type &a,Type &b);76.77.template<class Type>78.void BubbleSort(Type a[],int n);79.80.int main()81.{82.int n = 3;//物品数83.int c = 30;//背包容量84.int p[] = {0,45,25,25};//物品价值下标从1开始85.int w[] = {0,16,15,15};//物品重量下标从1开始86.int bestx[4];//最优解87.88. cout<<"背包容量为:"<<c<<endl;89. cout<<"物品重量和价值分别为:"<<endl;90.91.for(int i=1; i<=n; i++)92. {93. cout<<"("<<w[i]<<","<<p[i]<<") ";94. }95. cout<<endl;96.97. cout<<"背包能装下的最大价值为:"<<Knapsack(p,w,c,n,bestx)<<endl;98. cout<<"此背包问题最优解为:"<<endl;99.for(int i=1; i<=n; i++)100. {101. cout<<bestx[i]<<" ";102. }103. cout<<endl;104.return 0;105.}106.107.template<class Typew,class Typep>108.Typep Knap<Typew,Typep>::Bound(int i)//计算节点所相应价值的上界109.{110. Typew cleft = c-cw; //剩余容量高111. Typep b = cp; //价值上界112.//以物品单位重量价值递减序装填剩余容量113.while(i<=n && w[i]<=cleft)114. {115. cleft -= w[i];116. b += p[i];117. i++;118. }119.120.//装填剩余容量装满背包121.if(i<=n)122. {123. b += p[i]/w[i]*cleft;124. }125.126.return b;127.}128.129.//将一个活节点插入到子集树和优先队列中130.template<class Typew,class Typep>131.void Knap<Typew,Typep>::AddLiveNode(Typep up,Typep cp,Typew cw,bool ch,int lev)132.{133. bbnode *b = new bbnode;134. b->parent = E;135. b->LChild = ch;136.137. HeapNode<Typep,Typew> N;138. N.uprofit = up;139. N.profit = cp;140. N.weight = cw;141. N.level = lev;142. N.ptr = b;143.144. H->Insert(N);145.}146.147.//优先队列式分支限界法,返回最大价值,bestx返回最优解148.template<class Typew,class Typep>149.Typep Knap<Typew,Typep>::MaxKnapsack()150.{151. H = new MaxHeap<HeapNode<Typep,Typew>>(1000);152.153.//为bestx分配存储空间154. bestx = new int[n+1];155.156.//初始化157.int i = 1;158. E = 0;159. cw = cp = 0;160. Typep bestp = 0;//当前最优值161. Typep up = Bound(1); //价值上界162.163.//搜索子集空间树164.while(i!=n+1)165. {166.//检查当前扩展节点的左儿子节点167. Typew wt = cw + w[i];168.if(wt <= c)//左儿子节点为可行节点169. {170.if(cp+p[i]>bestp)171. {172. bestp = cp + p[i];173. }174. AddLiveNode(up,cp+p[i],cw+w[i],true,i+1); 175. }176.177. up = Bound(i+1);178.//检查当前扩展节点的右儿子节点179.if(up>=bestp)//右子树可能含有最优解180. {181. AddLiveNode(up,cp,cw,false,i+1);182. }183.184.//取下一扩展节点185. HeapNode<Typep,Typew> N;186. H->DeleteMax(N);187. E = N.ptr;188. cw = N.weight;189. cp = N.profit;190. up = N.uprofit;191. i = N.level;192. }193.194.//构造当前最优解195.for(int j=n; j>0; j--)196. {197. bestx[j] = E->LChild;198. E = E->parent;199. }200.return cp;201.}202.203.//返回最大价值,bestx返回最优解204.template<class Typew,class Typep>205.Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]) 206.{207.//初始化208. Typew W = 0; //装包物品重量209. Typep P = 0; //装包物品价值210.211.//定义依单位重量价值排序的物品数组212. Object *Q = new Object[n];213.for(int i=1; i<=n; i++)214. {215.//单位重量价值数组216. Q[i-1].ID = i;217. Q[i-1].d = 1.0*p[i]/w[i];218. P += p[i];219. W += w[i];220. }221.222.if(W<=c)223. {224.return P;//所有物品装包225. }226.227.//依单位价值重量排序228. BubbleSort(Q,n);229.230.//创建类Knap的数据成员231. Knap<Typew,Typep> K;232. K.p = new Typep[n+1];233. K.w = new Typew[n+1];234.235.for(int i=1; i<=n; i++)236. {237. K.p[i] = p[Q[i-1].ID];238. K.w[i] = w[Q[i-1].ID];239. }240.241. K.cp = 0;242. K.cw = 0;243. K.c = c;244. K.n = n;245.246.//调用MaxKnapsack求问题的最优解247. Typep bestp = K.MaxKnapsack();248.for(int j=1; j<=n; j++)249. {250. bestx[Q[j-1].ID] = K.bestx[j]; 251. }252.253.delete Q;254.delete []K.w;255.delete []K.p;256.delete []K.bestx;257.return bestp;258.}259.260.template<class Type>261.void BubbleSort(Type a[],int n)262.{263.//记录一次遍历中是否有元素的交换264.bool exchange;265.for(int i=0; i<n-1;i++)266. {267. exchange = false ;268.for(int j=i+1; j<=n-1; j++) 269. {270.if(a[j]<=a[j-1])271. {272. Swap(a[j],a[j-1]);273. exchange = true;274. }275. }276.//如果这次遍历没有元素的交换,那么排序结束277.if(false == exchange)278. {279.break ;280. }281. }282.}283.284.template <class Type>285.inline void Swap(Type &a,Type &b) 286.{287. Type temp = a;288. a = b;289. b = temp;290.}程序运行结果如图:。

相关文档
最新文档