分支定界解决旅行商问题

18 篇文章 0 订阅

由于旅行商问题是最小化问题,所以我们需要找下界。
这里我们每次选择最佳下界的节点进行扩展,用到最小堆。
开始的时候,我们这样计算下界lb:对于每一个城市i,求出从城市i到最近的两个城市的距离之和si,求出总和s
每次加入一条新的边,我们需要考虑把这条边放进去。对于这条边涉及到的顶点,我们要把这条边算入这两个顶点最近的两个城市间的距离。比如我们加入边a-d,那么a的两个城市有一个必须是d,d的两个城市有一个必须是a。

我们用序列来表示已经处理的边,(如a, a-b, a-b-c, a-b-c-d)

注意顶点分三种情况:

  • 1.没有在序列里面,直接加min(最小)和min2(第二小)

  • 2.在序列边上(开头或结尾)说明只有一条边纳入

    如果这条边正好是该顶点最小的边,那就这条边+第二小。
    如果不是,就这条边+最小。

  • 3.在序列内部,那就直接加两条边。

在实际处理过程中,我们可以用迭代方式来做,对于某一序列a-b-c-d,我们加入一个顶点e,形成a-b-c-d-e,注意到
d由序列边上到内部,e由序列外部进入序列内部。
对于d,我们把上次d的两条边的长度删掉(由于不知道是多少,所以我们要在上一次的时候就记下来,省的费劲再算),把这次d的两条边的长度加上。
对于e,我们删掉e的min和min2,把这次e的两条边的长度加上。
其他的也同理,除了第一次和最后一次特殊处理,只需要关心这两个点的改变。

为了方便实用min和min2,我们可以在开始的时候就计算出来。

最后求出来了再/2得到结果
首先是Node代码

package df;

import java.util.ArrayList;

public class Node {

	ArrayList<Integer>arr=new ArrayList<Integer>();
	int lb;//这里没有/2取上整,而是直接用
	int last_number;//指序列最后一个顶点的两条边的数据
	int set[];//为1表示已纳入
	
	public Node(ArrayList<Integer> arr, int lb, int last_number,int[]set) {
		super();
		this.arr = arr;
		this.lb = lb;
		this.last_number = last_number;
		this.set=set;
	}
	
	
	
	
	
	
}

下面是TSP代码

package df;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;



public class TSP {

	public static void set_edge(int m[][],int i,int value,int j)
	{
		m[i][j]=value;
		m[j][i]=value;
	}
	
	
	
	
	public static Node tsp_tree(int m[][])
	{
		int n=m.length;
		
		
		
		//小顶堆
		PriorityQueue<Node>pq=new PriorityQueue<Node>(new Comparator<Node>() {
		@Override
		public int compare(Node n1,Node n2) {
						
		if (n1.lb > n2.lb) {return 1;}
							
		if (n1.lb < n2.lb) {return -1;}
							
		return 0;
		
		}}
		);
		
		//min_index[i]指i连接的最小边的另一个顶点
		//min2_index[i]指i连接的第二小边的另一个顶点
		//本来应该初始化为-1,不过由于下面一定会改变,
		//所以不用初始化了
		int min_index[]=new int[n];
		int min2_index[]=new int[n];
		
		
		
		//我们要同时记录两个最小值的位置就行,最小值可以用矩阵直接求
				
		int min,min2;

		
		
		
		
		for(int i=0;i<n;i++)
		{	
			
			min=Integer.MAX_VALUE;
			min2=Integer.MAX_VALUE;
			for(int j=0;j<n;j++)
				if(m[i][j]<min)
				{
					min2=min;
					min=m[i][j];
					
					
					min2_index[i]=min_index[i];
					min_index[i]=j;
					
				}
				else if(m[i][j]<min2)
				{
					min2=m[i][j];
					
					
					min2_index[i]=j;
				}
		}
		
		
		/*注意分三种情况:
		 * 1.没有在已处理里面,直接加min和min2
		 * 2.在已处理里面,有一条边被处理
		 *       如果这条边正好是最小值,那就这条边+第二小
		 *       如果不是,就这条边+第一小
		 * 3.两条边都被处理,那就直接加两条边
		 */
		
		
		//为了方便,假设从0开始,且1(b)在2(c)前面
		ArrayList<Integer> arr=new ArrayList<Integer>();
		arr.add(0);
		int lb=0;
		for(int i=0;i<n;i++)
		{
			lb+=m[i][min_index[i]];
			lb+=m[i][min2_index[i]];
		}
		
		
		int last_number=0;
		last_number+=m[0][min_index[0]];
		last_number+=m[0][min2_index[0]];

		
		int set[]=new int[n];
		set[0]=1;
		
		Node node0=new Node(arr,lb,last_number,set);
		pq.add(node0);
		
		
		int max=Integer.MAX_VALUE;
		//可以乱找一个值作为初始answer,只要lb足够大
		Node answer=new Node(arr,max,last_number,set);
		
		
		
		while(pq.size()!=0)
		{
			Node node=pq.poll();
			//if(Math.ceil(node.lb/2.0)<answer.lb)
			if(node.lb<answer.lb)
			{
				for(int i=0;i<n;i++)
				{
					if(node.set[i]==0)
					{
						if(node.set[1]!=0||i!=2)
						//1在2前面
						{
							//每次计算lb,只需要看上次最后一个顶点、本次加入的顶点
							//假设路径还没有填充完整
							
							
							lb=node.lb;
							
					//处理第一个顶点
							
							
							int need=node.arr.get(node.arr.size()-1);
							
							if(node.arr.size()==1)
							{//need==0,只包含0
								if(min_index[need]==i)
									;
								else
								{
									//最小值抵消了
									//lb=lb-m[0][min_index[0]]-m[0][min2_index[0]];
									//lb=lb+m[0][min_index[0]]+m[i][0];
									lb=lb-m[need][min2_index[need]]+m[i][need];
								}
							}
							
							else
							{
								lb=lb-node.last_number;
								
								lb=lb+m[node.arr.get(node.arr.size()-2)][need]
									+m[need][i];
							}
							
					//处理第二个顶点
							if(need==min_index[i])
							{
								//lb加减所以不变
								last_number=m[i][min_index[i]]+m[i][min2_index[i]];
							}
								
							else
							{
								//最小值抵消了
								//lb=lb-m[i][min_index[i]]-m[i][min2_index[i]];
								//lb=lb+m[i][min_index[i]]+m[i][need];
								lb=lb-m[i][min2_index[i]]+m[i][need];
								last_number=m[i][min_index[i]]+m[i][need];
							}
							
							
							
							
							
							
							
							arr=new ArrayList<Integer>();
							arr.addAll(node.arr);
							arr.add(i);
							
							
							set=node.set.clone();
							set[i]=1;
							
							
							//if说明已经确定了,最后没有加入的点直接加入,然后再放上起点
							if(arr.size()==n-1)
							{	
								int j;
								//检查set,加入最后一个点j,以及起点0
								for( j=0;j<n;j++)
									if(set[j]==0)
										break;
								
								set[j]=1;
								
								
								need=arr.get(arr.size()-1);
								
								
								lb=lb-last_number;
								lb=lb+m[arr.get(arr.size()-2)][need]
										+m[need][j];
								
								
								
								
								
									
								
									
								lb=lb-m[j][min_index[j]]-m[j][min2_index[j]];
								lb=lb+m[need][j]+m[j][0];
									
								//下面要处理有关0的线段
								if(arr.get(1)==min_index[0])
								{
									lb=lb-m[0][min2_index[0]];
									lb=lb+m[j][0];
								}
								
								else
								{
									lb=lb-m[0][min_index[0]];
									lb=lb+m[j][0];
								}
								
								
								
								arr.add(j);
								arr.add(0);
								
								if(lb<answer.lb)
									answer=new Node(arr,lb,last_number,set);
								
							}
							
							
							
							
							
							else
							{
								Node no=new Node(arr,lb,last_number,set);
								pq.add(no);
							}
							
							
							
							
							
							
							
							
							
						}
					}
					
					
				}
			}
		}
		
		return answer;
	}
	
	
	
	public static void main(String[] args) {
		int [][]m=new int[5][5];
		for(int i=0;i<m.length;i++)
			m[i][i]=Integer.MAX_VALUE;
		
		
		set_edge(m,0,3,1);
		set_edge(m,0,1,2);
		set_edge(m,0,5,3);
		set_edge(m,0,8,4);

		set_edge(m,1,6,2);
		set_edge(m,1,7,3);
		set_edge(m,1,9,4);
		
		set_edge(m,2,4,3);
		set_edge(m,2,2,4);
		
		set_edge(m,3,3,4);

		Node answer=tsp_tree(m);
		//可以除以2得到路径长度
		System.out.println(answer.lb/2);
		

	}

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
旅行商问题是一个NP-hard问题,因此需要采用一些高效的算法来解决。其中分支限界法是一种常用的解决TSP问题的算法。 分支限界法的基本思想是:将问题的解空间树划分为多个子树,每个子树代表一个可行解,通过限界函数对每个子树进行枝,从而减少搜索空间,提高搜索效率。具体来说,分支限界法通过不断地分支和限界,逐步缩小搜索空间,最终找到最优解。 下面是旅行商问题分支限界法的Python实现: ```python import numpy as np class Node: def __init__(self, path, bound, cost): self.path = path self.bound = bound self.cost = cost def tsp_branch_bound(graph): n = graph.shape[0] nodes = [] for i in range(n): path = [i] bound = bound_func(graph, path) cost = 0 nodes.append(Node(path, bound, cost)) nodes.sort(key=lambda x: x.bound) best_path = None best_cost = np.inf while nodes: node = nodes.pop(0) if node.bound >= best_cost: continue if len(node.path) == n: cost = node.cost + graph[node.path[-1], node.path[0]] if cost < best_cost: best_cost = cost best_path = node.path + [node.path[0]] else: for i in range(n): if i not in node.path: path = node.path + [i] bound = bound_func(graph, path) cost = node.cost + graph[path[-2], i] nodes.append(Node(path, bound, cost)) nodes.sort(key=lambda x: x.bound) return best_path, best_cost def bound_func(graph, path): n = graph.shape[0] bound = 0 for i in range(n): if i not in path: min_cost = np.min(graph[i, :]) bound += min_cost for i in range(len(path) - 1): bound += graph[path[i], path[i+1]] return bound # 示例 graph = np.array([[0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]]) best_path, best_cost = tsp_branch_bound(graph) print("最短路径为:", best_path) print("最短路径长度为:", best_cost) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值