一、算法要求
有一批共n个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为Wi,且 。
装载问题要求确定是否有一个合理的装载方案可将这n个集装箱装上这2艘轮船。如果有,找出一种装载方案。
实质:求第一艘船的最优装载。
解空间树:子集树
1. 思路
用队列Q存放活结点表,Q中元素的值为活结点所相应的当前载重量。
当元素的值为-1时,表示队列已到达解空间树的同一层结点的尾部。
在算法的while循环中,首先检测当前扩展结点的左儿子结点是否为可行结点。
如果是则将其加入到活结点队列中;
然后将其右儿子结点加入到活结点队列中(右儿子结点一定是可行结点);
2个儿子结点都产生后,当前扩展结点被舍弃。
活结点队列中的队首元素被取出作为当前扩展结点,由于队列中每一层结点之后都有一个尾部标记-1,故在取队首元素时,活结点队列一定不空。
当取出的元素是-1时,再判断当前队列是否为空。
如果队列非空,则将尾部标记-1加入活结点队列,算法开始处理下一层的活结点。
二、完整代码
1. 主文件
main.cpp:
//Project1: 装载问题(最大堆)
#include "MaxHeap.h"
const int numMax = 4;
class bbnode;
template<class Type>
class HeapNode{
template<class Type>
friend void AddLiveNode(MaxHeap<HeapNode<Type>>& H, bbnode* E, Type wt, bool ch, int lev);
template<class Type>
friend Type MaxLoading(Type w[], Type c, int n, int bestx[]);
public:
operator Type() const { return uweight; }
private:
bbnode* ptr; //指向活节点在子集树中相应节点的指针
Type uweight; //活节点优先级(上界)
int level; //活节点在子集树中所处的层序号
};
class bbnode{
template<class Type>
friend void AddLiveNode(MaxHeap<HeapNode<Type>>& H, bbnode* E, Type wt, bool ch, int lev);
template<class Type>
friend Type MaxLoading(Type w[], Type c, int n, int bestx[]);
friend class AdjacencyGraph;
private:
bbnode* parent; //指向父节点的指针
bool LChild; //左儿子节点标识
};
template<class Type>
void AddLiveNode(MaxHeap<HeapNode<Type>>& H, bbnode* E, Type wt, bool ch, int lev);
template<class Type>
Type MaxLoading(Type w[], Type c, int n, int bestx[]);
//将活节点加入到表示活节点优先队列的最大堆H中
template<class Type>
void AddLiveNode(MaxHeap<HeapNode<Type>>& H, bbnode* E, Type wt, bool ch, int lev){
bbnode* b = new bbnode;
b->parent = E;
b->LChild = ch;
HeapNode<Type> numMax;
numMax.uweight = wt;
numMax.level = lev;
numMax.ptr = b;
H.Insert(numMax);
}
//优先队列式分支限界法,返回最优载重量,bestx返回最优解
template<class Type>
Type MaxLoading(Type w[], Type c, int n, int bestx[])
{
//定义最大的容量为1000
MaxHeap<HeapNode<Type>> H(1000);
//定义剩余容量数组
Type* r = new Type[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 = 0;//当前扩展节点
Type 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<Type> numMax;
H.DeleteMax(numMax);//非空
i = numMax.level;
E = numMax.ptr;
Ew = numMax.uweight - r[i - 1];
}
//构造当前最优解
for (int j = n; j > 0; j--){
bestx[j] = E->LChild;
E = E->parent;
}
return Ew;
}
int main(){
int x[numMax + 1];
float c = 70,
w[] = { 0,20,10,26,15 },//从1开始
bestw;
cout << "#The load of the ship is: " << c
<<"\n#The weight of the item is as follows:" << endl;
for (int i = 1; i <= numMax; i++){
cout << setw(3) << w[i];
}
cout << endl;
bestw = MaxLoading(w, c, numMax, x);
cout << "\n#The result of the algorithm is (maximum heap): " << endl;
for (int i = 1; i <= 4; i++){
if (x[i] == 1) {
cout << setw(3) << i;
}
}
cout << endl;
cout << "#The optimal loading weight is:" << bestw << endl;
return 0;
}
2. 头文件
MaxHeap.h:
#pragma once
#ifndef __MAXHEAP__
#define __MAXHEAP__
#include <iostream>
#include <iomanip>
using namespace std;
template<class T>
class MaxHeap{
public:
MaxHeap(int MaxHeapSize = 10);
~MaxHeap() { delete[] heap; }
int Size() const { return currentSize; }
MaxHeap<T>& Insert(const T& x); //增
MaxHeap<T>& DeleteMax(T& x); //删
void Initialize(T a[], int size, int ArraySize);
private:
int currentSize, MaxSize;
T* heap; // element array
};
template<class T>
MaxHeap<T>::MaxHeap(int MaxHeapSize){
MaxSize = MaxHeapSize;
heap = new T[MaxSize + 1];
currentSize = 0;
}
template<class T>
MaxHeap<T>& MaxHeap<T>::Insert(const T& x){
if (currentSize == MaxSize){
cout << "no space!" << endl;
return *this;
}
int i = ++currentSize;
while (i != 1 && x > heap[i / 2]){
// i不是根节点,且其值大于父节点的值,需要继续调整
heap[i] = heap[i / 2]; // 父节点下降
i /= 2; // 继续向上,搜寻正确位置
}
heap[i] = x;
return *this;
}
template<class T>
MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)
{// Set x to max element and delete max element from heap.
// check if heap is empty
if (currentSize == 0){
cout << "Empty heap!" << endl;
return *this;
}
x = heap[1]; // 删除最大元素
// 重整堆
T y = heap[currentSize--]; // 取最后一个节点,从根开始重整
// find place for y starting at root
int i = 1, // current node of heap
ci = 2; // child of i
while (ci <= currentSize){
// 使ci指向i的两个孩子中较大者
if (ci < currentSize && heap[ci] < heap[ci + 1]){
ci++;
}
if (y >= heap[ci]){
break; // 是,i就是y的正确位置,退出
}
heap[i] = heap[ci]; // 大于父节点的孩子节点上升
i = ci; // 向下一层,继续搜索正确位置
ci *= 2;
}
heap[i] = y;
return *this;
}
template<class T>
void MaxHeap<T>::Initialize(T a[], int size, int ArraySize){
delete[] heap;
heap = a;
currentSize = size;
MaxSize = ArraySize;
// 从最后一个内部节点开始,一直到根,对每个子树进行堆重整
for (int i = currentSize / 2; i >= 1; i--){
T y = heap[i]; // 子树根节点元素
int c = 2 * i;
while (c <= currentSize){
if (c < currentSize && heap[c] < heap[c + 1]){
c++;
}
if (y >= heap[c]){
break;
}
heap[c / 2] = heap[c];
c *= 2;
}
heap[c / 2] = y;
}
}
#endif
3. 效果展示
三、补充
解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。
活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量再加上剩余集装箱的重量之和。
优先队列中优先级最大的活结点成为下一个扩展结点。以结点x为根的子树中所有结点相应的路径的载重量不超过它的优先级。子集树中叶结点所相应的载重量与其优先级相同。
在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解。此时可终止算法。
文档供本人学习笔记使用,仅供参考。