优先队列式分支限界法 轮船装载问题(集装箱问题)

上一次介绍了集装箱装载问题的队列式分支限界法,本次将分享优先队列式分支限界法解决这个问题的算法。
Problem:
装载问题的问题提出是,有一批共n个集装箱要装上2艘载重量分别为 c1和c2 的轮船,其中集装箱i的重量为 wi,且(集装箱总重量和)w<=c1+c2 。问是否有一个合理的装载方案能将这n个集装箱装上这两艘轮船。
由于 王晓东先生著的《算法设计与分析》里面的代码由于缺少了最大堆类的描述,所以并不能运行,这里对该算法进行了增加与修改。若有需要的同学可以参考一下。

//优先队列式分支限界法_集装箱装载问题
public class PriorityBBloding {
// 优先队列式分支限界法,返回最优载重量 bestx返回最优解
static MaxHeap heap=new MaxHeap();
static int n;//集装箱数量
static class BBNode//解空间树的结点类
{
BBNode parent; //指向父结点的指针
boolean leftChild; //左儿子结点标志
BBNode(BBNode par,boolean ch)
{ parent=par;
leftChild=ch;
}
}

 static class HeapNode implements Comparable//堆的元素类型类
 {  BBNode liveNode; //指向活结点在子集树中相应结点的指针
     int  uweight; //活结点优先级(上界)
     int  level; //活结点在子集树中所处的层序号 
     HeapNode(BBNode node,int up,int lev)
       {//构造方法
         liveNode=node;
         uweight=up;
         level=lev;      }
      public int compareTo(Object x)
       {
        int xuw=((HeapNode)x).uweight;
        if(uweight<xuw)return -1;
        if(uweight==xuw)return 0;
        return 1;
       }
      public boolean equals(Object x)
       {
        return uweight==((HeapNode)x).uweight;
       }   
  }
 //从该方法可以看出该解法只是构建部分解空间树
 private static void addLiveNode(int up,int lev,BBNode par,boolean ch)//将一个新产生的BBNode类型活结点加入到子集树中,并将这个新结点插入到表示活结点优先队列的最大堆中
 {//将一个新产生的活结点插入到表示活结点优先队列的最大堆中
  BBNode b=new BBNode(par,ch);
  HeapNode node=new HeapNode(b,up,lev);
  heap.insert(node);
 }
 //用于存放活结点的最大堆类
 static class MaxHeap{
	 private static HeapNode[] maxheap;//最大堆的存储空间
	 private static int front;//堆顶引用,若堆不空,指向堆顶元素
	 private static int rear;//堆尾引用,若堆不空,指向堆尾元素的下一个位置
	 public MaxHeap() {//初始化最大堆
		 front=rear=0;
		 maxheap=new HeapNode[100];
	 }
	 //筛选法调整堆
	 //将以low为根结点的子树调整成大顶堆,low和high分别是待调整序列的上界和下界
	 static void sift(int low,int high) {
		 int i=low;//子树的根结点
		 int j=2*i+1;//j为i结点的左孩子
		 HeapNode temp=maxheap[i];
		 while(j<high) {//沿较大值的孩子结点向下筛选
			 if(j<high-1&&maxheap[j].uweight<maxheap[j+1].uweight) {
				 j++;//结点优先级进行比较,j为左右孩子结点的较大者
			 }
			 if(temp.uweight<maxheap[j].uweight) {//若父母结点值较小
				 maxheap[i]=maxheap[j];//孩子结点的较大者上移
				 i=j;
				 j=2*j+1;//对以被交换的子结点作为根结点所在的子树进行调整
			 }else {
				 j=high+1;//退出循环
			 }
		 }
		 maxheap[i]=temp;//当前子树的原根值调整后的位置
	 }
	 //创建堆算法
	 static void insertheapSort() {
		 int n=rear-front;//待加入堆的结点个数
		 HeapNode temp;
		 for(int i=n/2-1;i>=front;i--) {//创建堆
			 sift(i,n);
		 }
	 }
	 //取出堆顶元素,并且重新调整堆为最大堆的算法
	 static HeapNode removeheapSort() {
		 int n=rear-front;//
		 int i=n-1;//堆的最后一个结点
		 HeapNode temp=maxheap[front];//将堆中最小关键字值移到最前面
		 maxheap[front]=maxheap[i];
		 sift(front,i);//并且调整成堆
		 return temp;//返回最顶堆结点 
	 }
	//将堆元素node加入堆,并且调整堆
	 static void insert(HeapNode node) {
		 maxheap[rear]=node;
		 rear+=1;//修改尾指针
		 insertheapSort();//调整堆
	 }
	//将堆顶元素取出,并且调整堆
	public HeapNode removeMax() {
		HeapNode temp=removeheapSort();
		rear-=1;
		return temp;
	}
 }

 public static int maxLoading(int[] w,int c,int []bestx)
   {
       // 初始化
       BBNode A=null;
        BBNode e= A;        //当前扩展结点        
        int i=1;                     //当前扩展结点所处的层
        int ew=0;                    //扩展结点所相应的载重量
        int [ ]r=new int[n+1];        //定义剩余重量数组r
        for(int j=n-1;j>=0;j--)
         {r[j]=r[j+1]+w[j+1];}
 
      //搜索子集空间树
        while(i!=n+1)
        {//非叶结点,检查当前扩展结点的儿子结点
          if(ew+w[i]<=c)  
          //左儿子结点为可行结点
          addLiveNode(ew+w[i]+r[i],i+1,e,true);        
         // 右儿子结点总为可行结点 
          addLiveNode(ew+r[i],i+1,e,false); 
         //取下一个扩展结点       
        HeapNode node=(HeapNode)heap.removeMax();
        i=node.level;
        e=node.liveNode; //下一扩展结点
        ew=node.uweight-r[i-1];
       }
     for(int j=n;j>0;j--)
     {//构造当前最优解
      bestx[j]=(e.leftChild)?1:0;
      e=e.parent;
     }
    return ew;
   }
 
public static void main(String[] args) {
	n=5;//集装箱个数
	int []w= {0,60,40,10,30,50};//集装箱重量
	int c=120;//轮船载重量
	int[] bestx=new int[n+1];//最优解
	int a=maxLoading(w,c,bestx);
	System.out.println("最优载重量为:"+a);
	System.out.println("最优解为:");
	for(int i=1;i<n+1;i++) {
		System.out.print(bestx[i]+" ");
	}

}

}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值