【装载问题(最大堆)】“分支限界法”——《算法设计与分析(第五版)》


一、算法要求

有一批共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为根的子树中所有结点相应的路径的载重量不超过它的优先级。子集树中叶结点所相应的载重量与其优先级相同。

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

文档供本人学习笔记使用,仅供参考。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NI'CE'XIAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值