0034算法笔记分支限界法最优装载问题.docx
《0034算法笔记分支限界法最优装载问题.docx》由会员分享,可在线阅读,更多相关《0034算法笔记分支限界法最优装载问题.docx(19页珍藏版)》请在冰豆网上搜索。
0034算法笔记分支限界法最优装载问题
问题描述
有一批共个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为Wi,且
装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。
如果有,找出一种装载方案。
容易证明:
如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。
(1)首先将第一艘轮船尽可能装满;
(2)将剩余的集装箱装上第二艘轮船。
1、队列式分支限界法求解
在算法的循环体中,首先检测当前扩展结点的左儿子结点是否为可行结点。
如果是则将其加入到活结点队列中。
然后将其右儿子结点加入到活结点队列中(右儿子结点一定是可行结点)。
2个儿子结点都产生后,当前扩展结点被舍弃。
活结点队列中的队首元素被取出作为当前扩展结点,由于队列中每一层结点之后都有一个尾部标记-1,故在取队首元素时,活结点队列一定不空。
当取出的元素是-1时,再判断当前队列是否为空。
如果队列非空,则将尾部标记-1加入活结点队列,算法开始处理下一层的活结点。
节点的左子树表示将此集装箱装上船,右子树表示不将此集装箱装上船。
设bestw是当前最优解;ew是当前扩展结点所相应的重量;r是剩余集装箱的重量。
则当ew+r另外,为了确保右子树成功剪枝,应该在算法每一次进入左子树的时候更新bestw的值。
为了在算法结束后能方便地构造出与最优值相应的最优解,算法必须存储相应子集树中从活结点到根结点的路径。
为此目的,可在每个结点处设置指向其父结点的指针,并设置左、右儿子标志。
找到最优值后,可以根据parent回溯到根节点,找到最优解。
算法具体代码实现如下:
1、Queue.h
[cpp] viewplain copy
1.#include
2.using namespace std;
3.
4.template
5.class Queue
6.{
7. public:
8. Queue(int MaxQueueSize=50);
9. ~Queue(){delete [] queue;}
10. bool IsEmpty()const{return front==rear;}
11. bool IsFull(){return ( ( (rear+1) %MaxSize==front )?
1:
0);}
12. T Top() const;
13. T Last() const;
14. Queue& Add(const T& x);
15. Queue& AddLeft(const T& x);
16. Queue& Delete(T &x);
17. void Output(ostream& out)const;
18. int Length(){return (rear-front);}
19. private:
20. int front;
21. int rear;
22. int MaxSize;
23. T *queue;
24.};
25.
26.template
27.Queue:
:
Queue(int MaxQueueSize)
28.{
29. MaxSize=MaxQueueSize+1;
30. queue=new T[MaxSize];
31. front=rear=0;
32.}
33.
34.template
35.T Queue:
:
Top()const
36.{
37. if(IsEmpty())
38. {
39. cout<<"queue:
no element,no!
"<40. return 0;
41. }
42. else return queue[(front+1) % MaxSize];
43.}
44.
45.template
46.T Queue :
:
Last()const
47.{
48. if(IsEmpty())
49. {
50. cout<<"queue:
no element"<51. return 0;
52. }
53. else return queue[rear];
54.}
55.
56.template
57.Queue& Queue:
:
Add(const T& x)
58.{
59. if(IsFull())cout<<"queue:
no memory"<60. else
61. {
62. rear=(rear+1)% MaxSize;
63. queue[rear]=x;
64. }
65. return *this;
66.}
67.
68.template
69.Queue& Queue:
:
AddLeft(const T& x)
70.{
71. if(IsFull())cout<<"queue:
no memory"<72. else
73. {
74. front=(front+MaxSize-1)% MaxSize;
75. queue[(front+1)% MaxSize]=x;
76. }
77. return *this;
78.}
79.
80.template
81.Queue& Queue :
:
Delete(T & x)
82.{
83. if(IsEmpty())cout<<"queue:
no element(delete)"<84. else
85. {
86. front=(front+1) % MaxSize;
87. x=queue[front];
88. }
89. return *this;
90.}
91.
92.
93.template
94.void Queue :
:
Output(ostream& out)const
95.{
96. for(int i=rear%MaxSize;i>=(front+1)%MaxSize;i--)
97. out<98.}
99.
100.template
101.ostream& operator << (ostream& out,const Queue& x)
102.{x.Output(out);return out;}
2、6d3-1.cpp
[cpp] viewplain copy
1.//装载问题 队列式分支限界法求解
2.#include "stdafx.h"
3.#include "Queue.h"
4.#include
5.using namespace std;
6.
7.const int N = 4;
8.
9.template
10.class QNode
11.{
12. template
13. friend void EnQueue(Queue*>&Q,Type wt,int i,int n,Type bestw,QNode*E,QNode *&bestE,int bestx[],bool ch);
14.
15. template
16. friend Type MaxLoading(Type w[],Type c,int n,int bestx[]);
17.
18. private:
19. QNode *parent; //指向父节点的指针
20. bool LChild; //左儿子标识
21. Type weight; //节点所相应的载重量
22.};
23.
24.template
25.void EnQueue(Queue*>&Q,Type wt,int i,int n,Type bestw,QNode*E,QNode *&bestE,int bestx[],bool ch);
26.
27.template
28.Type MaxLoading(Type w[],Type c,int n,int bestx[]);
29.
30.int main()
31.{
32. float c = 70;
33. float w[] = {0,20,10,26,15};//下标从1开始
34. int x[N+1];
35. float bestw;
36.
37. cout<<"轮船载重为:
"<38. cout<<"待装物品的重量分别为:
"<39. for(int i=1; i<=N; i++)
40. {
41. cout<42. }
43. cout<44. bestw = MaxLoading(w,c,N,x);
45.
46. cout<<"分支限界选择结果为:
"<47. for(int i=1; i<=4; i++)
48. {
49. cout<50. }
51. cout<52. cout<<"最优装载重量为:
"<53.
54. return 0;
55.}
56.
57.//将活节点加入到活节点队列Q中
58.template
59.void EnQueue(Queue*>&Q,Type wt,int i,int n,Type bestw,QNode*E,QNode *&bestE,int bestx[],bool ch)
60.{
61. if(i == n)//可行叶节点
62. {
63. if(wt == bestw)
64. {
65. //当前最优装载重量
66. bestE = E;
67. bestx[n] = ch;
68. }
69. return;
70. }
71. //非叶节点
72. QNode *b;
73. b = new QNode;
74. b->weight = wt;
75. b->parent = E;
76. b->LChild = ch;
77. Q.Add(b);
78.}
79.
80.template
81.Type MaxLoading(Type w[],Type c,int n,int bestx[])
82.{//队列式分支限界法,返回最优装载重量,bestx返回最优解
83. //初始化
84. Queue*> Q; //活节点队列
85. Q.Add(0); //同层节点尾部标识
86. int i = 1; //当前扩展节点所处的层
87. Type Ew = 0, //扩展节点所相应的载重量
88. bestw = 0, //当前最优装载重量
89. r = 0; //剩余集装箱重量
90.
91. for(int j=2; j<=n; j++)
92. {
93. r += w[j];
94. }
95.
96. QNode *E = 0, //当前扩展节点
97. *bestE; //当前最优扩展节点
98.
99. //搜索子集空间树
100. while(true)
101. {
102. //检查左儿子节点
103. Type wt = Ew + w[i];
104. if(wt <= c)//可行节点
105. {
106. if(wt>bestw)
107. {
108. bestw = wt;
109. }
110. EnQueue(Q,wt,i,n,bestw,E,bestE,bestx,true);
111. }
112.
113. //检查右儿子节点
114. if(Ew+r>bestw)
115. {
116. EnQueue(Q,Ew,i,n,bestw,E,bestE,bestx,false);
117. }
118. Q.Delete(E);//取下一扩展节点
119.
120. if(!
E)//同层节点尾部
121. {
122. if(Q.IsEmpty())
123. {
124. break;
125. }
126. Q.Add(0); //同层节点尾部标识
127. Q.Delete(E); //取下一扩展节点
128. i++; //进入下一层
129. r-=w[i]; //剩余集装箱重量
130. }
131. Ew =E->weight; //新扩展节点所对应的载重量
132. }
133.
134. //构造当前最优解
135. for(int j=n-1; j>0; j--)
136. {
137. bestx[j] = bestE->LChild;
138. bestE = bestE->parent;
139. }
140. return bestw;
141.}
程序运行结果如图:
2、优先队列式分支限界法求解
解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。
活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量再加上剩余集装箱的重量之和。
优先队列中优先级最大的活结点成为下一个扩展结点。
以结点x为根的子树中所有结点相应的路径的载重量不超过它的优先级。
子集树中叶结点所相应的载重量与其优先级相同。
在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解。
此时可终止算法。
算法具体代码实现如下:
1、MaxHeap.h
[cpp] viewplain copy
1.template
2.class MaxHeap
3.{
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& Insert(const T& x); //增
19. MaxHeap& DeleteMax(T& x); //删
20.
21. void Initialize(T a[], int size, int ArraySize);
22.
23. private:
24. int CurrentSize, MaxSize;
25. T *heap; // element array
26.};
27.
28.template
29.MaxHeap:
:
MaxHeap(int MaxHeapSize)
30.{// Max heap constructor.
31. MaxSize = MaxHeapSize;
32. heap = new T[MaxSize+1];
33. CurrentSize = 0;
34.}
35.
36.template
37.MaxHeap& MaxHeap:
:
Insert(const T& x)
38.{// Insert x into the max heap.
39. if (CurrentSize == MaxSize)
40. {
41. cout<<"no space!
"<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
60.MaxHeap& MaxHeap:
:
DeleteMax(T& x)
61.{// Set x to max element and delete max element from heap.
62. // check if heap is empty
63. if (CurrentSize == 0)
64. {
65. cout<<"Empty heap!
"<66. return *this;
67. }
68.
69. x = heap[1]; // 删除最大元素
70. // 重整堆
71. T y = heap[CurrentSize--]; // 取最后一个节点,从根开始重整
72.
73. // find place for y starting at root
74. int i = 1, // current node of heap
75. ci = 2; // child of i
76.
77. while (ci <= CurrentSize)
78. {
79. // 使ci指向i的两个孩子中较大者
80. if (ci < CurrentSize && heap[ci] < heap