java语言解决旅行售货员问题(分支限界法)

2023-11-11 22:40

本文主要是介绍java语言解决旅行售货员问题(分支限界法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1、什么是旅行售货员问题

1.1基本介绍

2.问题描述

3.代码实现 


1、什么是旅行售货员问题

旅行售货员问题(travelling salesman problem)是一类组合最优化问题,设有一个售货员从城市1出发,到城市2,3,…,n去推销货物,最后回到城市1.假定任意两个城市i,j间的距离dij(dij=dji)是已知的,问他应沿着什么样的路线走,才能使走过的路线最短?容易看出,中国邮递员问题要求走遍所有“线”,而后者要求走遍所有“点”,旅行售货员问题就是在一个完全网络中,找出一个具有最小权的哈密顿圈,寻求旅行售货员问题的有效算法似乎是没有希望的,它属于NP完全类,一个可行的办法是首先求一个哈密顿圈,然后适当修改,以得到具较小权的另一个哈密顿圈,旅行售货员问题有着明显的实际意义,除售货员之外,邮局里负责到各个信箱取信的邮递员,以及去各个分局送邮件的汽车等都会类似地遇到这个问题,还有一些问题表面上似乎与之无关,而实质上却可以归结为旅行售货员问题求解,如计算机线路问题、无中间存储的工件加工问题等 [1]  。

1.1基本介绍

设有p个城镇,已知每两个城镇之间的距离,一个售货员从某一城镇出发巡回售货,问这个售货员应如何选择路线,能使每个城镇经过一次且仅一次,最后返回到出发地,而使总的行程最短?这个问题称为旅行售货员问题。容易看出,旅行售货员问题就是在一个赋权完全图中找一个具有最小权的Hamilton圈,我们称这种圈为最优Hamilton圈。

除旅行售货员问题之外,邮局中负责到各个信箱取信的邮递员,以及去各个分局送邮件的汽车等都会类似遇到这种问题,还有一些问题表面上似乎与之无关,而实质上却可以归结为旅行售货员问题来解决,既然这个问题有着如此广泛的应用,那么找一个求解最优Hamilton圈的有效算法就成为一件非常重要的事 [2]  

2.问题描述


某售货员要到若干城市去推销商品,已知各城市之间的路程(或旅费)。他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程( 或旅费)最小。各个城市之间可能是有向连通的、无向连通的、以及存在某个城市不连通的情况,你的程序应该能够处理所有可能的情况。如下图表示各个城市间无向连通。

 输入:
第一行为一个整数n(n<=10),表示城市的总个数。接下来是一个n*n的矩阵,用来表示城市间的连通情况以及花费,例如path[i][j]=len,len=-1表示从城市i到城市j没有通路,len>0表示从i到j的路程长度为len。对于上面图示的问题我们可以按照下面方式输入:

4
-1 30 6 4
30 -1 5 10
6 5 -1 20
4 10 20 -1

输出:25

3.代码实现 


import java.util.Scanner;
public class ts
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
int n=0;//结点的个数
String line=s.nextLine();//读入n
n=Integer.parseInt(line);
a=new float[n][n];
int []vv=new int[n];for(int i=0;i<n;i++)
{
line=s.nextLine();
String []sArray=line.split(" ");
for(int j=0;j<sArray.length;j++)
{
a[i][j]=Integer.parseInt(sArray[j]);
}
}
System.out.println(bbTsp(vv));
}
static float [][]a;
private static class HeapNode implements Comparable
{
float lcost,//子树费用下界
cc,//当前费用
rcost;//X[s:n-1]中顶点最小出边费用和
int s;//根节点到当前结点的路径为X[0:s]
int []x;//需要进一步搜索的结点是x[s+1:n-1]
//HeapNode的构造函数
HeapNode(float lc,float ccc,float rc,int ss,int []xx)
{
lcost=lc;
cc=ccc;
s=ss;
x=xx;
}//HeapNode 构造函数
public int compareTo(Object x)
{
float xlc=((HeapNode)x).lcost;
if(lcost<xlc)
return -1;
if(lcost==xlc)
return 0;
return 1;
}
}
public static int  bbTsp(int []v)
{
int n=v.length;
MinHeap heap=new MinHeap(100);
float []minOut=new float[n];//minOut[i]是顶点i的最小出边费用
float minSum=0;//最小出边费用和
//计算最小出边费用和
for(int i=0;i<n;i++)
{
float min=Float.MAX_VALUE;
for(int j=0;j<n;j++)
{
if(a[i][j]!=-1&&a[i][j]<min)
min=a[i][j];//有回路
}//for j
if(min==Float.MAX_VALUE)
{
return -1;//无回路
}
minOut[i]=min;
minSum+=min;
}
int []x=new int[n];
for(int i=0;i<n;i++)
{
x[i]=i;
}
HeapNode enode=new HeapNode(0,0,minSum,0,x);
float bestc=Float.MAX_VALUE;
//搜索排列空间树
while(enode!=null&&enode.s<n-1)
{
//System.out.println(bestc);
x=enode.x;
if(enode.s==n-2)//叶子结点
{
if(a[x[n-2]][x[n-1]]!=-1&&
a[x[n-1]][1]!=-1||
bestc==Float.MAX_VALUE)//当前最优解还不存在的情况
{
bestc=enode.cc+a[x[n-2]][x[n-1]]+a[x[n-1]][0];
enode.cc=bestc;
enode.lcost=bestc;
enode.s++;
heap.put(enode);
}
}
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.put(node);
}
}
}
}
enode=(HeapNode)heap.removeMin();
}
for(int i=0;i<n;i++)
v[i]=x[i];
return (int)bestc;
}
public static class MinHeap
{
private HeapNode[] heapArray; // 堆容器
private int maxSize; // 堆的最大大小
private int currentSize=0; // 堆大小
//构造函数
public MinHeap(int _maxSize)
{
maxSize = _maxSize;
heapArray = new HeapNode[maxSize];
currentSize = 0;
}
//自上而下调整
public void filterDown(int start, int endOfHeap)
{
int i = start;
int j = 2 * i + 1; // j是i的左子女位置
HeapNode temp = heapArray[i];
while (j <= endOfHeap)
{ // 检查是否到最后位置
if (j < endOfHeap // 让j指向两子女中的小者
&& heapArray[j].cc > heapArray[j + 1].cc)
{
j++;
}
if (temp.cc <= heapArray[j].cc)
{ // 小则不做调整
break;
} else
{ // 否则小者上移,i,j下降
heapArray[i] = heapArray[j];
i = j;
j = 2 * j + 1;
}
}
heapArray[i] = temp;
}//filterDown//自下而上的调整:从结点start开始到0为止,自下向上比较,如果子女的值小于双亲结点的值则互相交换
public void filterUp(int start)
{
int j = start;
int i = (j - 1) / 2;
HeapNode temp = heapArray[j];while (j > 0)
{ // 沿双亲结点路径向上直达根节点
if (heapArray[i].cc <= temp.cc)
{// 双亲结点值小,不调整
break;
} else {// 双亲结点值大,调整
heapArray[j] = heapArray[i];
j = i;
i = (i - 1) / 2;
}
heapArray[j] = temp; // 回送
}
}//filterUp//插入结点
public void put(HeapNode node)
{
HeapNode newNode = node;
heapArray[currentSize] = newNode;
filterUp(currentSize);
currentSize++;
}
//删除堆中的最小值
public HeapNode removeMin()
{
HeapNode root = heapArray[0];
heapArray[0] = heapArray[currentSize - 1];
currentSize--;
filterDown(0, currentSize - 1);
return root;
}
}
}

 实例输入

这篇关于java语言解决旅行售货员问题(分支限界法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/393300

相关文章

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

SpringBoot中配置Redis连接池的完整指南

《SpringBoot中配置Redis连接池的完整指南》这篇文章主要为大家详细介绍了SpringBoot中配置Redis连接池的完整指南,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以... 目录一、添加依赖二、配置 Redis 连接池三、测试 Redis 操作四、完整示例代码(一)pom.

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析