分支界限法-旅行售货员问题

博客围绕售货员旅行问题展开,该问题是售货员从驻地出发,遍历若干城市各一次后返回驻地,需确定一条使总路程或总旅费最小的路线,还包含相关代码及运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述
某售货员要到若干城市去推销商品,已知各城市之间的路程(旅费),他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程(总旅费)最小。
分支界限法
代码

package FenZhiJieXianFa;
import java.util.Collections;
import java.util.LinkedList;
 
/**
 * 旅行售货员问题--优先队列式分支限界法
 *
 */
public class LvXingShouhuoyuan {
	float[][] a;//图G的邻接矩阵
	public LvXingShouhuoyuan(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;//节点数
		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;
	}
	public static void main(String[] args) {
		//int n=4;
		//float[][] a={{0,0,0,0,0},{0,-1,30,6,4},{0,30,-1,5,10},{0,6,5,-1,20},{0,4,10,20,-1}};//a下标从1开始,0用来凑数;-1表示不同,1表示连通
		int n=5;
		float[][] a={{0,0,0,0,0,0},{0,-1,5,-1,7,9},{0,5,-1,10,3,6},{0,-1,10,-1,8,-1},{0,7,3,8,-1,4},{0,9,6,-1,4,-1}};//a下标从1开始,0用来凑数;-1表示不同,1表示连通
		LvXingShouhuoyuan b=new LvXingShouhuoyuan(a);
		int []v=new int[n+1];
		System.out.println("最短回路长为:"+b.bbTsp(v));
		System.out.print("最短回路为:");
		for(int i=1;i<=n;i++){
			System.out.print(v[i]+" ");
		}
	}
}
/*
==============================================================
测试数据:
int n=4;
float[][] a={{0,0,0,0,0},{0,-1,30,6,4},{0,30,-1,5,10},{0,6,5,-1,20},{0,4,10,20,-1}};
输出:
最短回路长为:25.0
最短回路为:1 3 2 4 
=======================================================================
测试数据:
int n=5;
float[][] a={{0,0,0,0,0,0},{0,-1,5,-1,7,9},{0,5,-1,10,3,6},{0,-1,10,-1,8,-1},{0,7,3,8,-1,4},{0,9,6,-1,4,-1}};
输出:
最短回路长为:36.0
最短回路为:1 5 2 3 4 
 */ 

运行结果

最短回路长为:36.0
最短回路为:1 5 2 3 4 
### 分支限界法解决旅行售货员问题的C语言实现 以下是使用分支限界法解决旅行售货员问题的C语言代码示例。该算法基于优先队列式分支限界法,采用限界函数 `lcost` 来优化搜索方向,并通过剪枝策略减少不必要的计算[^1]。 ```c #include <stdio.h> #include <limits.h> #include <stdlib.h> #define N 4 // 城市数量 #define INF INT_MAX int a[N][N] = { {0, 10, 15, 20}, {10, 0, 35, 25}, {15, 35, 0, 30}, {20, 25, 30, 0} }; // 距离矩阵 typedef struct Node { int level; // 当前节点所在的层次 int path[N]; // 当前路径 int cost; // 当前路径的费用 int bound; // 当前节点的限界值 } Node; // 计算限界函数 int calculateBound(int path[], int level) { int bound = 0; int i, j; // 添加从当前路径到其他未访问城市的最小代价 for (i = 0; i <= level; i++) { if (a[path[i]][path[i + 1]] == INF) return INF; bound += a[path[i]][path[i + 1]]; } // 添加从最后一个城市回到起点的代价 if (level == N - 2 && a[path[level]][path[0]] != INF) bound += a[path[level]][path[0]]; else if (level == N - 2 && a[path[level]][path[0]] == INF) return INF; // 添加从当前未访问城市到其他未访问城市的最小代价 for (i = 0; i < N; i++) { int min = INF; for (j = 0; j < N; j++) { if (a[i][j] < min && i != j) min = a[i][j]; } bound += min; } return bound; } // 比较函数用于优先队列排序 int compare(const void *a, const void *b) { return ((Node *)a)->bound - ((Node *)b)->bound; } // 主算法 void TSP_BranchAndBound() { Node queue[1000]; int num = 0; Node root; root.level = -1; for (int i = 0; i < N; i++) root.path[i] = -1; root.cost = 0; root.bound = 0; int i = 0; queue[num++] = root; int min_cost = INF; int best_path[N]; while (num > 0) { qsort(queue, num, sizeof(Node), compare); Node node = queue[0]; queue[0] = queue[num - 1]; num--; if (node.level == -1) { node.level = 0; node.path[0] = 0; node.bound = calculateBound(node.path, node.level); queue[num++] = node; continue; } if (node.bound < min_cost) { int v = node.path[node.level]; for (int i = 0; i < N; i++) { if (i != v) { int flag = 1; for (int j = 0; j <= node.level; j++) { if (node.path[j] == i) { flag = 0; break; } } if (flag == 1) { Node child = node; child.level = node.level + 1; child.path[child.level] = i; child.cost = node.cost + a[v][i]; child.bound = calculateBound(child.path, child.level); if (child.level == N - 1) { if (a[i][0] != INF && child.cost + a[i][0] < min_cost) { min_cost = child.cost + a[i][0]; for (int j = 0; j < N; j++) best_path[j] = child.path[j]; } } else { if (child.bound < min_cost) queue[num++] = child; } } } } } } printf("最短路径为: "); for (int i = 0; i < N; i++) printf("%d ", best_path[i]); printf("0\n"); printf("最短路径的花费为: %d\n", min_cost); } int main() { TSP_BranchAndBound(); return 0; } ``` 上述代码实现了分支限界法解决旅行售货员问题的核心逻辑。程序通过构造优先队列来选择最优子树进行扩展,并利用限界函数剪枝以减少不必要的计算[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值