0034算法笔记——【分支限界法】最优装载问题

本文介绍了如何使用队列式和优先队列式分支限界法解决最优装载问题。通过策略优化,首先尽可能填满第一艘船,再装第二艘。详细探讨了算法流程,包括结点的左子树表示装船,右子树表示不装,以及剪枝条件。同时提供了算法代码实现,包括Queue.h和MaxHeap.h文件。
摘要由CSDN通过智能技术生成

        问题描述

      有一批共个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为Wi,且装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。如果有,找出一种装载方案。 

     容易证明:如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。 
     (1)首先将第一艘轮船尽可能装满;
     (2)将剩余的集装箱装上第二艘轮船。 

     1、队列式分支限界法求解

      在算法的循环体中,首先检测当前扩展结点的左儿子结点是否为可行结点。如果是则将其加入到活结点队列中。然后将其右儿子结点加入到活结点队列中(右儿子结点一定是可行结点)。2个儿子结点都产生后,当前扩展结点被舍弃。

     活结点队列中的队首元素被取出作为当前扩展结点,由于队列中每一层结点之后都有一个尾部标记-1,故在取队首元素时,活结点队列一定不空。当取出的元素是-1时,再判断当前队列是否为空。如果队列非空,则将尾部标记-1加入活结点队列,算法开始处理下一层的活结点。

     节点的左子树表示将此集装箱装上船,右子树表示不将此集装箱装上船。设bestw是当前最优解;ew是当前扩展结点所相应的重量;r是剩余集装箱的重量。则当ew+r<bestw时,可将其右子树剪去,因为此时若要船装最多集装箱,就应该把此箱装上船。另外,为了确保右子树成功剪枝,应该在算法每一次进入左子树的时候更新bestw的值。

     为了在算法结束后能方便地构造出与最优值相应的最优解,算法必须存储相应子集树中从活结点到根结点的路径。为此目的,可在每个结点处设置指向其父结点的指针,并设置左、右儿子标志。

     找到最优值后,可以根据parent回溯到根节点,找到最优解。

     算法具体代码实现如下:

     1、Queue.h

#include<iostream>
using namespace std;

template <class T>
class Queue
{
	public:
		Queue(int MaxQueueSize=50);
		~Queue(){delete [] queue;}
		bool IsEmpty()const{return front==rear;}
		bool IsFull(){return ( (  (rear+1)  %MaxSize==front )?1:0);}
		T Top() const;
		T Last() const;
		Queue<T>& Add(const T& x);
		Queue<T>& AddLeft(const T& x);
		Queue<T>& Delete(T &x);
		void Output(ostream& out)const;
		int Length(){return (rear-front);}
	private:
		int front;
		int rear;
		int MaxSize;
		T *queue;
};

template<class T>
Queue<T>::Queue(int MaxQueueSize)
{
	MaxSize=MaxQueueSize+1;
	queue=new T[MaxSize];
	front=rear=0;
}

template<class T >
T Queue<T>::Top()const
{
	if(IsEmpty())
	{
		cout<<"queue:no element,no!"<<endl;
		return 0;
	}
	else return queue[(front+1) % MaxSize];
}

template<class T>
T Queue<T> ::Last()const
{
	if(IsEmpty())
	{
		cout<<"queue:no element"<<
好的,装载问题是经典的分支限界算法应用场景之一。下面是一个 Java 实现的分支限界法装载问题求解代码示例: ```java import java.util.*; // 装载问题节点类 class Node { int level; // 当前节点所处的层级 int weight; // 当前已装载的货物重量 int value; // 当前已装载的货物价值 boolean[] flag; // 标记当前货物是否被装载 public Node(int level, int weight, int value, boolean[] flag) { this.level = level; this.weight = weight; this.value = value; this.flag = flag; } } public class BranchAndBound { public static int maxWeight = 100; // 最大装载重量 public static int[] weights = {20, 15, 30, 10, 5}; // 货物重量 public static int[] values = {40, 25, 50, 15, 10}; // 货物价值 public static int n = 5; // 货物数量 // 分支限界法求解装载问题 public static int knapsack() { Queue<Node> queue = new PriorityQueue<>(new Comparator<Node>() { @Override public int compare(Node o1, Node o2) { return o2.value - o1.value; } }); // 优先队列用于存储分支节点 boolean[] flag = new boolean[n]; // 标记货物是否被装载 int max = 0; // 最大价值 Node root = new Node(0, 0, 0, flag); queue.offer(root); // 将根节点加入队列 while (!queue.isEmpty()) { Node node = queue.poll(); // 取出队列头节点 if (node.level == n) { // 到达叶子节点 if (node.value > max) { // 更新最大价值 max = node.value; } continue; } // 不装载当前货物的节点 boolean[] noFlag = Arrays.copyOf(node.flag, n); Node noNode = new Node(node.level + 1, node.weight, node.value, noFlag); queue.offer(noNode); // 装载当前货物的节点 if (node.weight + weights[node.level] <= maxWeight) { boolean[] yesFlag = Arrays.copyOf(node.flag, n); yesFlag[node.level] = true; Node yesNode = new Node(node.level + 1, node.weight + weights[node.level], node.value + values[node.level], yesFlag); queue.offer(yesNode); } } return max; } public static void main(String[] args) { int result = knapsack(); System.out.println("最大价值为:" + result); } } ``` 代码中的 `Node` 类表示分支节点,包含当前节点所处的层级、当前已装载的货物重量、当前已装载的货物价值以及标记当前货物是否被装载的布尔数组。`knapsack()` 方法用于求解装载问题,`maxWeight` 表示最大装载重量,`weights` 表示各货物重量,`values` 表示各货物价值,`n` 表示货物数量。代码中使用优先队列来存储分支节点,节点按价值从大到小排序,以便优先扩展价值大的节点。在每个节点处,都分别生成装载当前货物和不装载当前货物的两个子节点,并将它们加入优先队列。最后,遍历完所有叶子节点后,返回最大价值即可。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值