分支限界解决旅行商tsp问题

15 篇文章 0 订阅

本人的算法大作业

参考博客:

http://blog.csdn.net/qq_32400847/article/details/51813606

http://www.cnblogs.com/cielosun/p/5654582.html

http://www.cnblogs.com/cielosun/p/5654582.html

基本思想

1.按宽度优先策略遍历解空间树
2.在遍历过程中,对处理的每个结点vi,根据界限函数,估计沿该结点向下搜索所可能达到的完全解的目标函数的可能取值范围—界限bound(vi)=[downi, upi]
3.从中选择使目标函数取的极值(最大、最小)的结点优先进行宽度优先搜索,从而不断调整搜索方向,尽快找到问题解。
各结点的界限函数bound(vi)=[downi, upi]是解决问题的关键,通常依据具体问题而定。常见的两种分支限界法是队列式分支限界法和优先队列式分支限界法,它们分别按照队列先进先出的原则和优先队列中规定的优先级选取下一个节点为扩展节点。分支限界法与回溯法的不同之处在于求解目标和搜索方式的不同。回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解;回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。我们以TSP问题为例用分支限界法解决。TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市,要求路径的总和最小。考虑下图所示的情况及其代价矩阵,假定起始城市为1号城市。

算法描述

输入:城市数量n和城市之间路程的代价矩阵

输出:城市之间固定起点的哈密顿回路的最短路程

1)计算初始上下界,使用暴力排序对代价矩阵进行排序计算出下界,使用贪心算法计算出上界。

2)定义一个类保存开始节点、结束节点、已遍历的城市及其数量、已遍历的路径长度、当前节点的目标函数值,类中定义一个方法计算当前节点的目标函数值,由当前已遍历的路径的二倍加上进出其余未遍历城市的最短路径,再加上出当前城市的最短路径和进起始城市的最短路径,取其二分之一,是当前节点的目标函数值,即当前节点的下界。

3)把起始节点加入优先队列,当优先队列不为空时,取出当前优先级最高的节点,若当前已遍历n-1个城市,则计算当前路径长度加上最后一个节点的路径长度和记为ans,若ans小于所有目标函数值,则跳出循环并把ans作为最优解输出。若ans不小于所有目标函数值,则更新上界和可行解继续循环。若当前已遍历城市个数小于n-1,则判断当前取出节点的子节点的目标函数是否小于上界,若小于则添加进优先队列,否则继续判断下一个子节点。



样例矩阵



搜索树


结果:


代码:

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>

#define INF 100000
#define MAX 100
using namespace std;

int result;
int weight[MAX][MAX];

class City{
public:
	City(){	
	}
	int m_iUpWeight;		//上界 
	int m_iDownWeight;		//下界 
	int m_iCount;			//输入的总的城市数量 
	int visitedCitys[MAX];	//已走过的城市 
};
City city;

class Nodes{
public:
	int visitedCity[MAX];	//已走过的城市 
	int numbers;			//已走过的城市数量 
	int endCity;			//已走过的最后一个城市  
	int startCity;			//已走过的第一个城市 
	int sumLength;			//已走过的长度 
	int aimFun;				//当前节点的目标函数值 
	
	Nodes(){
	    for(int i=0;i<MAX;++i){
	    	visitedCity[i]=0;
	    }
	    visitedCity[0]=1;
		numbers=1;
		endCity=0;
		startCity=0;
		sumLength=0;
		aimFun=0;
	}
	
	bool operator <(const Nodes &p )const {  
        return aimFun>p.aimFun;  
    } 
	
	int calAimFun(){
		int ret=sumLength*2;
		int min1=INF,min2=INF;
		for(int i=0;i<city.m_iCount;++i){
			if(visitedCity[i]==0 && min1>weight[i][startCity]){
				min1=weight[i][startCity];
			}
		}
		for(int i=0;i<city.m_iCount;++i){
			if(visitedCity[i]==0 && min2>weight[endCity][i]){
				min2=weight[endCity][i];
			}
		}
		ret+=(min1+min2);
		for(int i=0;i<city.m_iCount;++i){
			if(visitedCity[i]==0){
				min1=INF;
				min2=INF;
				for(int j=0;j<city.m_iCount;++j){
					if(min1>weight[i][j]){
						min1=weight[i][j];
					}
				}
				for(int j=0;j<city.m_iCount;++j){
					if(min2>weight[j][i]){
						min2=weight[j][i];
					}
				}
				ret+=(min1+min2);		
			}
		}
		return ret%2==0?ret/2:(ret/2+1);
	}
}; 


//优先队列,比较大小然后输出最先的值 
priority_queue<Nodes> q;

void input(){
	scanf("%d",&city.m_iCount);
	for(int i=0;i<city.m_iCount;++i){
		for(int j=0;j<city.m_iCount;++j){
			if(i==j)
				weight[i][j]=INF;
			else 
				cin>>weight[i][j];
		}
	}
}

//确定下界 
int getDownWeight(){
	int downWeight=0;
	int temp[city.m_iCount];
	for(int i=0;i<city.m_iCount;++i){
		memcpy(temp,weight[i],sizeof(temp));
		sort(temp,temp+city.m_iCount);
		downWeight+=(temp[0]+temp[1])/2;
	}	
	return downWeight;
}
//贪心法确定上界 
int dfs(int now_p,int count,int sumLength){
	if(count==city.m_iCount-1){
		return sumLength+weight[now_p][0];
	}
	int min=INF;
	int next_p=-1;
	for(int i=0;i<city.m_iCount;++i){
		if(city.visitedCitys[i]==0&&min>weight[now_p][i]){
			min=weight[now_p][i];
			next_p=i;
		}	
	}
	city.visitedCitys[next_p]=1;
	return dfs(next_p,count+1,sumLength+min);
}

int getUpWeight(){
	memset(city.visitedCitys,0,sizeof(city.visitedCitys));  //标记已走过的城市 
	city.visitedCitys[0]=1;
	return dfs(0,0,0);
}

int solve(){
	
	city.m_iUpWeight=getUpWeight();
	city.m_iDownWeight=getDownWeight();
	
	Nodes node;
	node.aimFun=city.m_iDownWeight;
	
	int ret=INF;
	q.push(node);
	while(!q.empty()){
		Nodes temp=q.top();
//cout<<"拿出:"<<temp.endCity<<endl; 
		q.pop();
		if(temp.numbers==city.m_iCount-1){
			int p=-1;
			for(int i=0;i<city.m_iCount;++i){
				if(0==temp.visitedCity[i]){
					p=i;
					break;
				}
			}
			int ans=temp.sumLength+weight[temp.endCity][p]+weight[p][temp.startCity];
			Nodes judge=q.top();
			
			if(ans<=judge.aimFun){
				ret=min(ans,ret);
				break;
			}else{
				city.m_iUpWeight=min(city.m_iUpWeight,ans);
				ret=min(ret,ans);
				continue;	
			}
		}
		Nodes next;
		for(int i=0;i<city.m_iCount;++i){
			if(temp.visitedCity[i]==0){
				next.endCity=i;
				next.startCity=temp.startCity;
				next.numbers=temp.numbers+1;
				next.sumLength=temp.sumLength+weight[temp.endCity][i];
				
				for(int k=0;k<city.m_iCount;++k)next.visitedCity[k]=temp.visitedCity[k];
				next.visitedCity[i]=1;
				next.aimFun=next.calAimFun();
				
//cout<<next.endCity<<" 的目标函数是 "<<next.aimFun<<endl;
				if(next.aimFun>=city.m_iUpWeight)continue;
				q.push(next);
//cout<<next.endCity<<" 添加"<<endl;
			}
		}
	}
	return ret;
} 

int main(){
	
	memset(weight,0,sizeof(weight));
	input();
	city.m_iDownWeight=getDownWeight();
	city.m_iUpWeight=getUpWeight();
	//cout<<"city.m_iDownWeight "<<city.m_iDownWeight<< "city.m_iUpWeight "<<city.m_iUpWeight<<endl; 	
	int ret=solve();
	//cout<<"最优解为:"<<endl;
	cout<<ret<<endl;
	return 0; 
}


  • 27
    点赞
  • 170
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: TSP问题是指旅行商问题,即给定一些城市和它们之间的距离,求出一条经过每个城市一次且回到起点的最短路径。 优先队列分支限界法是一种求解TSP问题的算法。它采用分支限界法的思想,将问题分解为若干个子问题,并通过优先队列来管理这些子问题。每次从队列中取出一个子问题进行求解,求解过程中通过剪枝操作来减少搜索空间,直到找到最优解。 具体来说,优先队列分支限界法的步骤如下: 1. 将起点作为当前节点,将所有未访问的城市作为子节点加入优先队列中。 2. 从队列中取出一个子节点,计算从起点到该节点的路径长度,并记录下已经访问的城市。 3. 如果已经访问了所有城市,则更新最优解,并回溯到上一个节点。 4. 如果当前路径长度已经大于最优解,则剪枝,回溯到上一个节点。 5. 如果当前路径长度小于最优解,则将该节点的所有子节点加入优先队列中。 6. 重复步骤2-5,直到队列为空。 通过优先队列分支限界法,可以在较短的时间内求解TSP问题,并得到最优解。 ### 回答2: 优先队列分支限界法解决TSP问题的一种有效方法。随着问题规模的增大,暴力搜索算法出现了指数级别的组合数爆炸,极大地降低了算法效率。这正是我们需要寻找更加高效的算法求解TSP问题的原因所在。 优先队列分支限界法中,我们选择一组包含起始点的城市,所有其他城市未被访问。在这个基础上,以第一个节点为父亲节点,强行访问下一个节点——等价于一条边——而后又强行访问下一个未被访问的节点,直到达到出发点,并生成一条恰好经过每个节点恰好一次的回路。在这个基础上,我们就开始构建搜索树。 优先队列分支限界法时,我们会记录下每一条最小的边,然后以每个节点离根节点的距离来排序所有候选节点。我们逐个扩展并生成搜索树的分支。 以搜索树中的深度表示路径的长度,每当到达分支的时候,我们会对未被访问的节点使用评价函数评价所有子节点,并记录下评价值最小的那个节点。我们将最小的那个节点放入一个优先队列中,并重复这个步骤直到我们更新了全局最优值,或者我们已经检查了优先队列中的所有节点。 在优先队列分支限界法的过程中,我们认为所有未被访问的节点都是可行的。其次,我们选择最小的边,在每个节点中重新评价最优状态。这个方法比普通的BB算法更加节省存储空间,并且可以按照任意顺序处理节点。最后,在这个算法中,我们能够使用优先队列来进行节点扩展排序,并且在每个节点中进行子节点的评价。这个过程提高了搜索树的宽度和深度,缩短了搜索时间,能够帮助我们更快的找到最优解。 ### 回答3: 优先队列分支限界法是一种求解TSP问题的有效策略。针对旅行商问题,我们需要遍历所有可能的路径并找到最短路径。优先队列分支限界法就是根据已知的最优路径,通过剪枝和分支策略进行搜索,来寻找更优的路径。 1.剪枝过程。每次扩展新的节点时,我们可以计算当前节点的下界,如果它已经比已知的最优解要大,那么就可以剪枝,不需要再往下搜索。剪枝的目的是减少搜索空间,加快算法的运算速度。 2.分支过程。当我们找到一个新的节点时,我们需要根据其相邻节点的距离和路径长度,计算出从这个节点出发的所有可能的路径,并将它们加入到优先队列中。然后按照路径长度从小到大的顺序,依次处理队列中的每个节点,选取其中最优的一个路径进行扩展,不断重复这个过程,直到找到最优解为止。 通过优先队列分支限界法,我们可以高效地求解TSP问题,将搜索空间缩小到较小的范围,提高了算法的运算速度。但是该算法最坏情况下仍需遍历所有可能的路径,因此当问题规模较大时,仍需要适当的优化措施来提高算法的效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值