算法设计与分析(旅行售货员问题---分支限界法java实现)

旅行售货员问题——分支限界法实现

(1)题目需求分析
某售货员要到若干城市去推销商品,已知各城市之间的路程(或旅费)。他要选定一条从驻地出发,经过每个城市一次,最后回到驻地的路线,使总的路程(或总旅费)最小。
旅行售货员问题的解空间树是一颗排序树。与前面关于子集树的讨论类似,实现对排列树搜索的优先队列式分支限界法也可以用两种不同的实现方式。一种是仅使用一个优先队列来存储活结点。优先队列中的每个活结点都存储从根到该活结点的相应路径。另一种是用优先队列来存储活结点,并同时存储当前已构造出的部分排列树。在这种方式下,优先队列中的活结点不必再存储从根到该活结点的相应路径,这条路径可在必要时从存储的部分排列树中获得。
(2)程序功能代码(JAVA实现):

public class BBTSP {
	float[][] a;//图G的邻接矩阵
	public BBTSP(float[][] a){
		this.a=a;
	}
	public static class HeapNode implements Comparable{
		float lcost;//子树费用的下界
		float cc;//当前费用
		float rcost;//x[s:n-1]中顶点最小出边费用和
		int s;//根节点到当前节点的路径为x[0:s]
		int[] x;//需要进一步搜索的顶点是x[s+1:n-1]
		
		//构造方法
		public HeapNode(float lc,float ccc,float rc,int ss,int[] xx){
			lcost=lc;
			cc=ccc;
			s=ss;
			x=xx;
		}
		public int compareTo(Object x){
			float xlc=((HeapNode) x).lcost;
			if(lcost<xlc) return -1;
			if(lcost==xlc) return 0;
			return 1;
		}
	}
	
	public float bbTsp(int[] v){//解决旅行售货员问题的优先队列分支界限法
		int n=v.length-1;//节点数
		//MinHeap heap=new MinHeap();
		LinkedList<HeapNode> heap=new LinkedList<HeapNode>();
		//minOut[i]=i的最小出边费用
		float[] minOut=new float[n+1];
		float minSum=0;//最小出边费用和
		for(int i=1;i<=n;i++){//针对每个节点,找到最小出边
			//计算minOut[i]和minSum
			float min=Float.MAX_VALUE;
			for(int j=1;j<=n;j++)
				if(a[i][j]<Float.MAX_VALUE&&a[i][j]<min)
					min=a[i][j];
			
			if(min==Float.MAX_VALUE)
				return Float.MAX_VALUE;//没有回路
			minOut[i]=min;
			minSum+=min;
		}
		
		//初始化
		int[] x=new int[n];
		for(int i=0;i<n;i++)
			x[i]=i+1;
		HeapNode enode=new HeapNode(0,0,minSum,0,x);
		float bestc=Float.MAX_VALUE;
		
		//搜索排列空间树
		while(enode!=null&&enode.s<n-1){
			//非叶节点
			x=enode.x;
			if(enode.s==n-2){
				//当前扩展结点是叶节点的父节点
				//再加两条边构成回路
				//所构成回路是否优于当前最优解
				if(a[x[n-2]][x[n-1]]!=-1&&a[x[n-1]][1]!=-1&&enode.cc+a[x[n-2]][x[n-1]]+a[x[n-1]][1]<bestc){
					//找到费用更小的回路
					bestc=enode.cc+a[x[n-2]][x[n-1]]+a[x[n-1]][1];
					enode.cc=bestc;
					enode.lcost=bestc;
					enode.s++;
					heap.add(enode);
					Collections.sort(heap);
				}
			}else{//内部结点
				//产生当前扩展结点的儿子结点
				for(int i=enode.s+1;i<n;i++){
					if(a[x[enode.s]][x[i]]!=-1){
						//可行儿子结点
						float cc=enode.cc+a[x[enode.s]][x[i]];
						float rcost=enode.rcost=minOut[x[enode.s]];
						float b=cc+rcost;//下界
						if(b<bestc){
							//子树可能含有最优解,结点插入最小堆
							int[] xx=new int[n];
							for(int j=0;j<n;j++)
								xx[j]=x[j];
							xx[enode.s+1]=x[i];
							xx[i]=x[enode.s+1];
							HeapNode node=new HeapNode(b,cc,rcost,enode.s+1,xx);
							heap.add(node);
							Collections.sort(heap);
						}
					}
				}
				
			}
			
			//取下一个扩展结点
			enode=heap.poll();
		}
		//将最优解复制到v[1...n]
		for(int i=0;i<n;i++)
			v[i+1]=x[i];
		return bestc;
	}
发布了22 篇原创文章 · 获赞 4 · 访问量 1573

猜你喜欢

转载自blog.csdn.net/qq_42711899/article/details/103549916