实验五、优先队列式分支限界法解装载问题

合集下载

《算法设计与分析》考试题目及答案(DOC)

《算法设计与分析》考试题目及答案(DOC)

《算法设计与分析》考试题目及答案(DOC)D. 预排序与递归调用7. 回溯法在问题的解空间树中,按(D)策略,从根结点出发搜索解空间树。

A.广度优先B. 活结点优先 C.扩展结点优先 D. 深度优先8. 分支限界法在问题的解空间树中,按(A)策略,从根结点出发搜索解空间树。

A.广度优先B. 活结点优先 C.扩展结点优先 D. 深度优先9. 程序块(A)是回溯法中遍历排列树的算法框架程序。

A.B.C.D. void backtrack (int t){if (t>n) output(x);elsefor (int i=t;i<=n;i++) {swap(x[t], x[i]);if (legal(t)) backtrack(t+1); swap(x[t], x[i]);}}void backtrack (int t){if (t>n) output(x);elsefor (int i=0;i<=1;i++) {x[t]=i;if (legal(t)) backtrack(t+1); }}10. 回溯法的效率不依赖于以下哪一个因素?(C )A.产生x[k]的时间;B.满足显约束的x[k]值的个数;C.问题的解空间的形式;D.计算上界函数bound的时间;E.满足约束函数和上界函数约束的所有x[k]的个数。

F.计算约束函数constraint的时间;11. 常见的两种分支限界法为(D)A. 广度优先分支限界法与深度优先分支限界法;B. 队列式(FIFO)分支限界法与堆栈式分支限界法;C. 排列树法与子集树法;D. 队列式(FIFO)分支限界法与优先队列式分支限界法;12. k带图灵机的空间复杂性S(n)是指(B)A.k带图灵机处理所有长度为n的输入时,在某条带上所使用过的最大方格数。

B.k带图灵机处理所有长度为n的输入时,在k条带上所使用过的方格数的总和。

C.k带图灵机处理所有长度为n的输入时,在k条带上所使用过的平均方格数。

第6章 分支限界法(1-例子)

第6章 分支限界法(1-例子)
5
6.3 装载问题
3. 算法的改进
// 检查右儿子结点 // 检查左儿子结点 Type wt = Ew + w[i]; if (wt <= c) { // 可行结点 右儿子剪枝
if (Ew + r > bestw && i < n) Q.Add(Ew); // 可能含最优解 Q.Delete(Ew);// 取下一扩展结点 提前更新 bestw
5. 优先队列式分支限界法
解装载问题的优先队列式分支限界法用最大优先队列存 储活结点表。 活结点x在优先队列中的优先级定义为从根结点到结点x的 路径所相应的载重量再加上剩余集装箱的重量之和。 优先队列中优先级最大的活结点成为下一个扩展结点。 在优先队列式分支限界法中,一旦有一个叶结点成为当 前扩展结点,则可以断言该叶结点所相应的解即为最优解。 此时可终止算法。
while6.3 (true)装载问题 { // 检查左儿子结点 2. 队列式分支限界法 if (Ew + w[i] <= c) // x[i] = 1 EnQueue(Q, Ew + w[i], bestw, i, n); // 右儿子结点总是可行的 EnQueue(Q, Ew, bestw, i, n); // x[i] = 0 Q.Delete(Ew); // 取下一扩展结点 if (Ew == -1) { // 同层结点尾部 if (Q.IsEmpty()) return bestw; Q.Add(-1); // 同层结点尾部标志 Q.Delete(Ew); // 取下一扩展结点 i++; // 进入下一层 } }
if (wt > bestw) bestw = wt; // 加入活结点队列 if (i < n) Q.Add(wt); }

用分支限界算法解装载问题详解

用分支限界算法解装载问题详解

用分支限界算法解装载问题详解一、实验目的1、理解分支限界法的概念,掌握分支限界法的基本要素。

2、掌握设计分支限界法的一般步骤,针对具体问题,能应用分支限界法求解二、实验内容1、问题描述:有一批共个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为Wi,且w1+…+wn<= C1+ C2; 装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。

如果有,找出一种装载方案。

2、数据输入:文件输入或键盘输入。

3、要求:1)完成上述问题的队列式分支限界法解决问题,时间为1 次课。

2)独立完成实验及实验报告。

三、实验步骤1、理解方法思想和问题要求。

2、采用编程语言实现题目要求。

3、上机输入和调试自己所写的程序。

4、附程序主要代码:#include <bits/stdc++、h>using namespace std;class MaxHeapQNode{public: MaxHeapQNode*parent; int lchild; int weight; int lev;};structcmp{ bool operator()(MaxHeapQNode *&a, MaxHeapQNode *&b)const { return a->weight < b->weight; }};int n;int c;int bestw;int w[100];int bestx[100];voidInPut(){ scanf("%d %d", &n, &c); for(int i =1; i <= n;++i)scanf("%d", &w[i]);}voidAddAliveNode(priority_queue<MaxHeapQNode *,vector<MaxHeapQNode *>, cmp> &q, MaxHeapQNode *E, int wt, int i, int ch){ MaxHeapQNode *p = new MaxHeapQNode; p->parent = E; p->lchild = ch; p->weight = wt; p->lev = i +1; q、push(p);}voidMaxLoading(){ priority_queue<MaxHeapQNode *,vector<MaxHeapQNode *>, cmp > q; // 大顶堆 //定义剩余重量数组r int r[n +1]; r[n] = 0; for(int j = n-j)r[j] = r[j +1] + w[j +1]; int i =1; MaxHeapQNode *E; int Ew = 0; while(i != n +1){ if(Ew + w[i] <= c){ AddAliveNode(q, E, Ew + w[i] + r[i], i,1); } AddAliveNode(q, E, Ew + r[i], i, 0); //取下一节点 E = q、top(); q、pop(); i = E->lev; Ew = E->weight1]; } bestw = Ew; for(int j = n; j > 0;j){ bestx[j] = E->lchild; E = E->parent; }}void OutPut(){ printf("最优装载量为 %d\n", bestw); printf("装载的物品为 \n"); for(int i =1; i <= n; ++i)if(bestx[i] ==1)printf("%d ", i);}int main(){ InPut(); MaxLoading(); OutPut();}5、实验结果:4、装载问题实验分析:1、将wt<=c和Ew+r>=bestw作为限界判定。

分支限界法求解装载问题实验报告

分支限界法求解装载问题实验报告
void EnQueue(int wt,int& bestw, int i, int n, Queue*E,Queue*&bestE, bool ch) // 该函数负责加入活结点 { // 如果不是叶结点,则将结点权值 wt 加入队列 Q
if (i == n) {
if(wt == bestw) {
friend Type MaxLoading_three(Type *,Type,int,int *); private:
QNode* parent;// 指向父结点的指针 bool LChild; //左儿子标志 Type weight; //结点所相应的载重量 };
四、算法实现
void EnQueue(int wt,int& bestw, int i, int n, Queue*E,Queue*&bestE, bool ch) // 该函数负责加入活结点 { // 如果不是叶结点,则将结点权值 wt 加入队列 Q
} 构造最优解: 为了在算法结束后能方便地构造出与最优值相应的最优解,算法必须存储相应子集树中从活 结点到根结点的路径。为此目的,可在每个结点处设置指向其父结点e> class QNode {
friend void EnQueue(queue<QNode <Type> * >&,Type,int,int,Type,QNode <Type> *,QNode <Type> * &,int*,bool);
if (IsEmpty()) break; if(i<n) Add(-1, NULL, 0); // 同层结点的尾部 Delete(E); // 取下一扩展结点 i++; // 进入下一层 r -= w[i]; } Ew = E->weight; } //构造当前最优解 for(j = n -1; j>0; j--) { bestx[j] = bestE->LChild; bestE = bestE ->parent; } return 0; }

实验五、优先队列式分支限界法解装载问题

实验五、优先队列式分支限界法解装载问题

实验五优先队列式分支限界法解装载问题09电信实验班I09660118 徐振飞一、实验题目实现书本P201所描述的优先队列式分支限界法解装载问题二、实验目的(1)掌握并运用分支限界法基本思想(2)运用优先队列式分支限界法实现装载问题(3)比较队列式分支限界法和优先队列式分支限界法的优缺点三、实验内容和原理(1)实验内容有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi,且∑=+≤niiccw121,要求确定是否有一个合理的装载方案可将这n个集装箱装上这2艘轮船。

如果有,请给出方案。

(2)实验原理解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。

活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量再加上剩余集装箱的重量之和。

优先队列中优先级最大的活结点成为下一个扩展结点。

优先队列中活结点x的优先级为x.uweight。

以结点x为根的子树中所有结点相应的路径的载重量不超过x.uweight。

子集树中叶结点所相应的载重量与其优先级相同。

因此在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解,此时终止算法。

上述策略可以用两种不同方式来实现。

第一种方式在结点优先队列的每一个活结点中保存从解空间树的根结点到该活结点的路径,在算法确定了达到最优值的叶结点时,就在该叶结点处同时得到相应的最优解。

第二种方式在算法的搜索进程中保存当前已构造出的部分解空间树,在算法确定了达到最优值的叶结点时,就可以在解空间树中从该叶结点开始向根结点回溯,构造出相应的最优解。

在下面的算法中,采用第二种方式。

四、源程序import parator;import java.util.Iterator;import java.util.PriorityQueue;import java.util.Scanner;public class test5 {public void addLiveNode(PriorityQueue<HeapNode> H,bbnode E,int wt,boolean ch,int lev){bbnode b = new bbnode(E,ch);HeapNode N = new HeapNode(b, wt, lev);H.add(N);}public int maxLoading(int w[],int c,int n,boolean bestx[]){PriorityQueue<HeapNode> H = new PriorityQueue(1000,new comp());/*生成最大堆*/int[] r = new int[n+1];r[n] = 0;for(int j=n-1;j>0;j--){r[j] = r[j+1] + w[j+1];}int i = 1;bbnode E = new bbnode(null,false);int Ew = 0;while(i!=n+1){if(Ew+w[i]<=c){addLiveNode(H, E, Ew+w[i]+r[i], true, i+1);}addLiveNode(H, E, Ew+r[i], false, i+1);HeapNode N;N=H.poll();i = N.level;E = N.ptr;Ew = N.uweight - r[i-1];}//构造最优解for(int j=n;j>0;j--){bestx[j] = E.Lchild;E = E.parent;}return Ew;}public static void main(String[] args){System.out.println("请输入物品总数:");Scanner sc1 = new Scanner(System.in);int n = sc1.nextInt();int[] w = new int[n+1];System.out.println("请输入物品重量:");Scanner sc2 = new Scanner(System.in);for(int i=1;i<=n;i++){w[i] = sc2.nextInt();}System.out.println("请输入箱子重量:");Scanner sc3 = new Scanner(System.in);int c1 = sc3.nextInt();int c2 = sc3.nextInt();boolean[] bestx = new boolean[100];test5 t = new test5();//处理第一个箱子System.out.println("first:"+t.maxLoading(w, c1, n, bestx));System.out.print("可装重为:");int count = 0;for(int i=1;i<=n;i++){if(bestx[i]){count++;System.out.print(w[i]+" "); /*输出一个可行方案*/ }}System.out.println();/*处理第二个箱子*/int m = n - count;int[] ww = new int[m+1];int k = 1;for(int i=1;i<=n;i++){if(!bestx[i]){ww[k] = w[i];k++;bestx[i] = false;}}System.out.println();System.out.println("second:"+t.maxLoading(ww, c2, m, bestx));System.out.print("可装重为:");for(int i=1;i<=m;i++){if(bestx[i]){System.out.print(ww[i]+" "); /*输出一个可行方案*/ }}}}/*堆结点类*/class HeapNode{bbnode ptr;int uweight;int level;public HeapNode(){}public HeapNode(bbnode ptr,int uweight,int level){this.ptr = ptr;this.uweight = uweight;this.level = level;}public String toString(){return ""+this.uweight;}}class bbnode{bbnode parent;boolean Lchild;public bbnode(bbnode node,boolean ch){this.parent = node;this.Lchild = ch;}}//定义比较器类class comp implements Comparator<HeapNode>{@Overridepublic int compare(HeapNode o1, HeapNode o2) {int dif = o1.uweight-o2.uweight;if(dif>0){return -1;}else if(dif==0){return 0;}else{return 1;}}}五、实验结果和分析a.输入格式说明:(1)首先输入物品总数量(2)第二栏输入所有物品重量(3)第三栏输入2个箱子的重量b.输出格式说明:(1)首先输出first的字样,后面的数字表示第一个箱子所能装载的最大重量,紧接着的一行输出一种可以选择装载的方案(2)Second字样后面的数字表示第二个箱子所能装载的最大重量,紧接着的一行输出一种可行方案经过分析,上述结果正确。

优先队列式分支限界法求解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文件中读取物品重量和价值。

算法——分支限界法(装载问题)

算法——分支限界法(装载问题)

算法——分⽀限界法(装载问题)对⽐回溯法回溯法的求解⽬标是找出解空间中满⾜约束条件的所有解,想必之下,分⽀限界法的求解⽬标则是找出满⾜约束条件的⼀个解,或是满⾜约束条件的解中找出使某⼀⽬标函数值达到极⼤或极⼩的解,即在某种意义下的最优解。

另外还有⼀个⾮常⼤的不同点就是,回溯法以深度优先的⽅式搜索解空间,⽽分⽀界限法则以⼴度优先的⽅式或以最⼩耗费优先的⽅式搜索解空间。

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

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

分⽀限界法解决了⼤量离散最优化的问题。

选择⽅法1.队列式(FIFO)分⽀限界法队列式分⽀限界法将活节点表组织成⼀个队列,并将队列的先进先出原则选取下⼀个节点为当前扩展节点。

2.优先队列式分⽀限界法优先队列式分⽀限界法将活节点表组织成⼀个优先队列,并将优先队列中规定的节点优先级选取优先级最⾼的下⼀个节点成为当前扩展节点。

如果选择这种选择⽅式,往往将数据排成最⼤堆或者最⼩堆来实现。

例⼦:装载问题有⼀批共n个集装箱要装上2艘载重量分别为c1,c2的轮船,其中集装箱i的重量为wi,且要求确定是否有⼀个合理的装载⽅案可将这n个集装箱装上这2艘轮船。

可证明,采⽤如下策略可以得到⼀个最优装载⽅案:先尽可能的将第⼀艘船装满,其次将剩余的集装箱装到第⼆艘船上。

代码如下://分⽀限界法解装载问题//⼦函数,将当前活节点加⼊队列template<class Type>void EnQueue(Queue<Type> &Q, Type wt, Type &bestw, int i, int n){if(i == n) //可⾏叶结点{if(wt>bestw) bestw = wt ;}else Q.Add(wt) ; //⾮叶结点}//装载问题先尽量将第⼀艘船装满//队列式分⽀限界法,返回最优载重量template<class Type>Type MaxLoading(Type w[],Type c,int n){//初始化数据Queue<Type> Q; //保存活节点的队列Q.Add(-1); //-1的标志是标识分层int i=1; //i表⽰当前扩展节点所在的层数Type Ew=0; //Ew表⽰当前扩展节点的重量Type bestw=0; //bestw表⽰当前最优载重量//搜索⼦集空间树while(true){if(Ew+w[i]<=c) //检查左⼉⼦EnQueue(Q,Ew+w[i],bestw,i,n); //将左⼉⼦添加到队列//将右⼉⼦添加到队列即表⽰不将当前货物装载在第⼀艘船EnQueue(Q,Ew,bestw,i,n);Q.Delete(Ew); //取下⼀个节点为扩展节点并将重量保存在Ewif(Ew==-1) //检查是否到了同层结束{if(Q.IsEmpty()) return bestw; //遍历完毕,返回最优值Q.Add(-1); //添加分层标志Q.Delete(Ew); //删除分层标志,进⼊下⼀层i++;}}}算法MaxLoading的计算时间和空间复杂度为O(2^n).上述算法可以改进,设r为剩余集装箱的重量,当Ew+r<=bestw的时候,可以将右⼦树剪去。

JX-算法分析与设计(六)分支限界法-8

JX-算法分析与设计(六)分支限界法-8

C
D
Cr<w2 不可行解
H
I
J
Cr<w3 K 不可行解
Cr=14 V=45 L x=(1,0,0)
w3=15, v3=25 Cr=0,V=50 50>45 x=(0,1,1)
说明:左子树表示选择该物品,右子树表示不选择。
5
分支限界法的基本思想(4)

回溯法与分支限界法(Branch and Bound)的区别
E.i=4, E.length=30 j=1 c[E.i][1] =∞, 不处理 j=2 c[E.i][2] =∞, 不处理 j=3 dist[3]=30+20=50, prev[3]=4 j=5 dist[5]=30+60=90, prev[5]=4
此时删除顶点4,然后选取顶点3作为扩展结点 E.i=3, E.length=50 j=1 c[E.i][1] =∞, 不处理 [(3,50), (5,90)] j=2 c[E.i][2] =∞, 不处理 j=4 c[E.i][4] =∞, 不处理 [(3,50), (5,60)] j=5 dist[5]=50+10=60, prev[5]=3 此时删除顶点3,然后选取顶点5作为扩展结点
4 3 E B
2
G 2
4
H 3 N
2
I 2 O
3
J P
13
[B, H] B, H 用上界函数剪枝掉
主要内容



分支限界法的基本思想 单源最短路径问题 装载问题 0-1背包问题 旅行售货员问题
14
单源最短路径问题(1)

单源最短路径问题:
给定带权有向图G =(V, E),其中每条边的权(Weight)是非负实数。 另外,还给定V中的一个顶点,称为源(Source)。现在要计算从源s到各 顶点的最短路径长度。路径长度是指路上各边权之和。
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

实验五优先队列式分支限界法解装载问题09电信实验班I09660118 徐振飞一、实验题目实现书本P201所描述的优先队列式分支限界法解装载问题二、实验目的(1)掌握并运用分支限界法基本思想(2)运用优先队列式分支限界法实现装载问题(3)比较队列式分支限界法和优先队列式分支限界法的优缺点三、实验内容和原理(1)实验内容有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi,且∑=+≤niiccw121,要求确定是否有一个合理的装载方案可将这n个集装箱装上这2艘轮船。

如果有,请给出方案。

(2)实验原理解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。

活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量再加上剩余集装箱的重量之和。

优先队列中优先级最大的活结点成为下一个扩展结点。

优先队列中活结点x的优先级为x.uweight。

以结点x为根的子树中所有结点相应的路径的载重量不超过x.uweight。

子集树中叶结点所相应的载重量与其优先级相同。

因此在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解,此时终止算法。

上述策略可以用两种不同方式来实现。

第一种方式在结点优先队列的每一个活结点中保存从解空间树的根结点到该活结点的路径,在算法确定了达到最优值的叶结点时,就在该叶结点处同时得到相应的最优解。

第二种方式在算法的搜索进程中保存当前已构造出的部分解空间树,在算法确定了达到最优值的叶结点时,就可以在解空间树中从该叶结点开始向根结点回溯,构造出相应的最优解。

在下面的算法中,采用第二种方式。

四、源程序import parator;import java.util.Iterator;import java.util.PriorityQueue;import java.util.Scanner;public class test5 {public void addLiveNode(PriorityQueue<HeapNode> H,bbnode E,int wt,boolean ch,int lev){bbnode b = new bbnode(E,ch);HeapNode N = new HeapNode(b, wt, lev);H.add(N);}public int maxLoading(int w[],int c,int n,boolean bestx[]){PriorityQueue<HeapNode> H = new PriorityQueue(1000,new comp());/*生成最大堆*/int[] r = new int[n+1];r[n] = 0;for(int j=n-1;j>0;j--){r[j] = r[j+1] + w[j+1];}int i = 1;bbnode E = new bbnode(null,false);int Ew = 0;while(i!=n+1){if(Ew+w[i]<=c){addLiveNode(H, E, Ew+w[i]+r[i], true, i+1);}addLiveNode(H, E, Ew+r[i], false, i+1);HeapNode N;N=H.poll();i = N.level;E = N.ptr;Ew = N.uweight - r[i-1];}//构造最优解for(int j=n;j>0;j--){bestx[j] = E.Lchild;E = E.parent;}return Ew;}public static void main(String[] args){System.out.println("请输入物品总数:");Scanner sc1 = new Scanner(System.in);int n = sc1.nextInt();int[] w = new int[n+1];System.out.println("请输入物品重量:");Scanner sc2 = new Scanner(System.in);for(int i=1;i<=n;i++){w[i] = sc2.nextInt();}System.out.println("请输入箱子重量:");Scanner sc3 = new Scanner(System.in);int c1 = sc3.nextInt();int c2 = sc3.nextInt();boolean[] bestx = new boolean[100];test5 t = new test5();//处理第一个箱子System.out.println("first:"+t.maxLoading(w, c1, n, bestx));System.out.print("可装重为:");int count = 0;for(int i=1;i<=n;i++){if(bestx[i]){count++;System.out.print(w[i]+" "); /*输出一个可行方案*/ }}System.out.println();/*处理第二个箱子*/int m = n - count;int[] ww = new int[m+1];int k = 1;for(int i=1;i<=n;i++){if(!bestx[i]){ww[k] = w[i];k++;bestx[i] = false;}}System.out.println();System.out.println("second:"+t.maxLoading(ww, c2, m, bestx));System.out.print("可装重为:");for(int i=1;i<=m;i++){if(bestx[i]){System.out.print(ww[i]+" "); /*输出一个可行方案*/ }}}}/*堆结点类*/class HeapNode{bbnode ptr;int uweight;int level;public HeapNode(){}public HeapNode(bbnode ptr,int uweight,int level){this.ptr = ptr;this.uweight = uweight;this.level = level;}public String toString(){return ""+this.uweight;}}class bbnode{bbnode parent;boolean Lchild;public bbnode(bbnode node,boolean ch){this.parent = node;this.Lchild = ch;}}//定义比较器类class comp implements Comparator<HeapNode>{@Overridepublic int compare(HeapNode o1, HeapNode o2) {int dif = o1.uweight-o2.uweight;if(dif>0){return -1;}else if(dif==0){return 0;}else{return 1;}}}五、实验结果和分析a.输入格式说明:(1)首先输入物品总数量(2)第二栏输入所有物品重量(3)第三栏输入2个箱子的重量b.输出格式说明:(1)首先输出first的字样,后面的数字表示第一个箱子所能装载的最大重量,紧接着的一行输出一种可以选择装载的方案(2)Second字样后面的数字表示第二个箱子所能装载的最大重量,紧接着的一行输出一种可行方案经过分析,上述结果正确。

六、实验心得和体会通过实验,了解了分支限界法的基本思想。

掌握了利用优先队列式分支限界法实现具体的装载问题。

由于本次实验利用java.util 包下的PriorityQueue代替算法中的最大堆,免去了编写实现最大堆的程序代码(但这并不表示我不会编写最大堆程序,在这次实验中,最大堆的实现并不是主要部分),所以本次实验实现的相对顺利。

存在的问题及分析:在此例中最合理的装载方法是第一个箱子装载重量为:10 50的物品,第二个箱子装载重量为20 20的物品。

分析:由于程序中先装载第一个箱子,然后再装载第二个箱子;而分支限界法一旦扩展结点到达叶子结点时,程序便终止退出。

所以在此例中当第一个箱子装满后(此时重量为10 20 30的物品已被装走),余下的物品重量只有50 20 两个,因此第二个箱子无法装满。

解决方法:可以对程序稍作改变,寻找所有可行的装载方案,然后利用装满率(装载量/箱子重量)来判断那种方案是最优的装载方案。

这样做必定会增加程序的时间和空间复杂度,当数据量很大时,不适合(此时可以改变输入数据的顺序去试探装载方案,然后人为确定一个相对最有的装载方案)。

相关文档
最新文档