TSP旅行商问题之分支界限法法优化

关注:新零云博客

文章目录

      • 关注:[新零云博客](https://www.xingly.cn)
    • @[toc]
  • @Author: 【新零云】数据中心 - 团队
  • @Date: 2020-06-28 19:18:34
  • @Last Modified by: XingLy.Cn
  • @Last Modified time: 2020-06-28 19:18:35
  • @Last Modified time: 2020-06-28 19:18:35
  • @本文采用创作共用版权 CC BY-NC-SA 3.0 CN 许可协议
  • ————————————————————————————
  • 正文如下
  • ==1.思想:== (IDEA)
  • ==2.如何编写?==(How to write?)
  • ==3.题目==(Topic)
  • ==4.代码==

@Author: 【新零云】数据中心 - 团队

@Date: 2020-06-28 19:18:34

@Last Modified by: XingLy.Cn

@Last Modified time: 2020-06-28 19:18:35

@Last Modified time: 2020-06-28 19:18:35

@本文采用创作共用版权 CC BY-NC-SA 3.0 CN 许可协议


————————————————————————————

正文如下

1.思想: (IDEA)

基本思想

  • 1.按宽度优先策略遍历解空间树

  • 2.在遍历过程中,对处理的每个结点vi,根据界限函数,估计向下搜索所可能的完全解的目标函数的取值范围
    界限bound(vi)=[downi, upi]

  • 3.从中选择使目标函数取的极值(最大、最小)的结点优先进行宽度优先搜索从而不断调整搜索方向,尽快找到问题解。

    各结点的界限函数bound(vi)=[downi, upi]是解决问题的关键,通常依据具体问题而定。

    常见的两种分支限界法是队列式分支限界法和优先队列式分支限界法,它们分别按照队列先进先出的原则和优先队列中规定

    的优先级选取下一个节点为扩展节点。


2.如何编写?(How to write?)

  1. 计算初始上下界,使用暴力排序对代价矩阵进行排序计算出下界,使用贪心算法计算出上界。
  2. 定义一个类保存开始节点、结束节点、已遍历的城市及其数量、已遍历的路径长度、当前节点的目标函数值,类中定义一个方法计算当前节点的目标函数值,由当前已遍历的路径的二倍加上进出其余未遍历城市的最短路径,再加上出当前城市的最短路径和进起始城市的最短路径,取其二分之一,是当前节点的目标函数值,即当前节点的下界。
  3. 把起始节点加入优先队列,当优先队列不为空时,取出当前优先级最高的节点,若当前已遍历n-1个城市,则计算当前路径长度加上最后一个节点的路径长度和记为ans,若ans小于所有目标函数值,则跳出循环并把ans作为最优解输出。若ans不小于所有目标函数值,则更新上界和可行解继续循环。若当前已遍历城市个数小于n-1,则判断当前取出节点的子节点的目标函数是否小于上界,若小于则添加进优先队列,否则继续判断下一个子节点。

3.题目(Topic)

TSP旅行商问题之分支界限法法优化_第1张图片
如图N=5,求最短路程与路径?

思路过程如下
TSP旅行商问题之分支界限法法优化_第2张图片

TSP旅行商问题之分支界限法法优化_第3张图片


4.代码

#include 
using namespace std;
#define NoEdge 1000
/*
# @Author: 【新零云】数据中心 - 团队
# @Date:   2020-06-28 19:18:34
# @Last Modified by:   XingLy.Cn
# @Last Modified time: 2020-06-28 19:18:35
# @Last Modified time: 2020-06-28 19:18:35
# @本文采用创作共用版权 CC BY-NC-SA 3.0 CN 许可协议
*/
 
struct MinHeapNode
{
	int lcost; //子树费用的下界
	int cc; //当前费用
	int rcost; //x[s:n-1]中顶点最小出边费用和
	int s; //根节点到当前节点的路径为x[0:s]
	int *x; //需要进一步搜索的顶点是//x[s+1:n-1]
	struct MinHeapNode *next;
};
 
int n; //图G的顶点数
int **a; //图G的邻接矩阵
//int   NoEdge;   //图G的无边标记
int cc; //当前费用
int bestc; //当前最小费用
MinHeapNode* head = 0; /*堆头*/
MinHeapNode* lq = 0; /*堆第一个元素*/
MinHeapNode* fq = 0; /*堆最后一个元素*/
 
int DeleteMin(MinHeapNode*&E)
{
	MinHeapNode* tmp = NULL;
	tmp = fq;
	// w = fq->weight ;
	E = fq;
	if(E == NULL)
		return 0;
	head->next = fq->next; /*一定不能丢了链表头*/
	fq = fq->next;
	// free(tmp) ;
	return 0;
}
 
int Insert(MinHeapNode* hn)
{
	if(head->next == NULL)
	{
		head->next = hn; //将元素放入链表中
		fq = lq = head->next; //一定要使元素放到链中
	}
	else
	{
		MinHeapNode *tmp = NULL;
		tmp = fq;
		if(tmp->cc > hn->cc)
		{
			hn->next = tmp;
			head->next = hn;
			fq = head->next; /*链表只有一个元素的情况*/
		}
		else
		{
			for(; tmp != NULL;)
			{
				if(tmp->next != NULL && tmp->cc > hn->cc)
				{
					hn->next = tmp->next;
					tmp->next = hn;
					break;
				}
				tmp = tmp->next;
			}
		}
		if(tmp == NULL)
		{
			lq->next = hn;
			lq = lq->next;
		}
	}
	return 0;
}
 
int BBTSP(int v[])
{
	//解旅行售货员问题的优先队列式分支限界法
 
	/*初始化最优队列的头结点*/
	head = (MinHeapNode*)malloc(sizeof(MinHeapNode));
	head->cc = 0;
	head->x = 0;
	head->lcost = 0;
	head->next = NULL;
	head->rcost = 0;
	head->s = 0;
	int *MinOut = new int[n + 1]; /*定义定点i的最小出边费用*/
	//计算MinOut[i]=顶点i的最小出边费用
	int MinSum = 0;//最小出边费用总合
	for(int i = 1; i <= n; i++)
	{
		int Min = NoEdge; /*定义当前最小值*/
		for(int j = 1; j <= n; j++)
			if(a[i][j] != NoEdge && /*当定点i,j之间存在回路时*/
			        (a[i][j] < Min || Min == NoEdge)) /*当顶点i,j之间的距离小于Min*/
				Min = a[i][j]; /*更新当前最小值*/
		if(Min == NoEdge)
			return NoEdge;//无回路
		MinOut[i] = Min;
		//printf("%d\n",MinOut[i]);/*顶点i的最小出边费用*/
		MinSum += Min;
		// printf("%d\n",MinSum); /*最小出边费用的总和*/
	}
 
 
	MinHeapNode *E = 0;
	E = (MinHeapNode*)malloc(sizeof(MinHeapNode));
	E->x = new int[n];
	// E.x=new int[n];
	for(int i = 0; i < n; i++)
		E->x[i] = i + 1;
	E->s = 0;
	E->cc = 0;
	E->rcost = MinSum;
	E->next = 0; //初始化当前扩展节点
	int bestc = NoEdge; /*记录当前最小值*/
	//搜索排列空间树
	while(E->s < n - 1)
	{
		//非叶结点
		if(E->s == n - 2)
		{
			//当前扩展结点是叶结点的父结点
			/*
			首先考虑s=n-2的情形,此时当前扩展结点是排列树中某个叶结点的父结点。如果该叶结点相应一条可行回路
			且费用小于当前最小费用,则将该叶结点插入到优先队列中,否则舍去该叶结点
			*/
			if(a[E->x[n - 2]][E->x[n - 1]] != NoEdge && /*当前要扩展和叶节点有边存在*/
			        a[E->x[n - 1]][1] != NoEdge && /*当前页节点有回路*/
			        (E->cc + a[E->x[n - 2]][E->x[n - 1]] + a[E->x[n - 1]][1] < bestc /*该节点相应费用小于最小费用*/
			         || bestc == NoEdge))
			{
				bestc = E->cc + a[E->x[n - 2]][E->x[n - 1]] + a[E->x[n - 1]][1]; /*更新当前最新费用*/
				E->cc = bestc;
				E->lcost = bestc;
				E->s++;
				E->next = NULL;
				Insert(E); /*将该页节点插入到优先队列中*/
			}
			else
				free(E->x);//该页节点不满足条件舍弃扩展结点
		}
		else
		{
			/*产生当前扩展结点的儿子结点
			当s
			for(int i = E->s + 1; i < n; i++)
				if(a[E->x[E->s]][E->x[i]] != NoEdge)
				{
					/*当前扩展节点到其他节点有边存在*/
					//可行儿子结点
					int cc = E->cc + a[E->x[E->s]][E->x[i]]; /*加上节点i后当前节点路径*/
					int rcost = E->rcost - MinOut[E->x[E->s]]; /*剩余节点的和*/
					int b = cc + rcost; //下界
					if(b < bestc || bestc == NoEdge)
					{
						//子树可能含最优解,结点插入最小堆
						MinHeapNode * N;
						N = (MinHeapNode*)malloc(sizeof(MinHeapNode));
						N->x = new int[n];
						for(int j = 0; j < n; j++)
							N->x[j] = E->x[j];
						N->x[E->s + 1] = E->x[i];
						N->x[i] = E->x[E->s + 1];/*添加当前路径*/
						N->cc = cc; /*更新当前路径距离*/
						N->s = E->s + 1; /*更新当前节点*/
						N->lcost = b; /*更新当前下界*/
						N->rcost = rcost;
						N->next = NULL;
						Insert(N); /*将这个可行儿子结点插入到活结点优先队列中*/
					}
				}
			free(E->x);
		}//完成结点扩展
		DeleteMin(E);//取下一扩展结点
		if(E == NULL)
			break; //堆已空
	}
	if(bestc == NoEdge)
		return NoEdge;//无回路
	for(int i = 0; i < n; i++)
		v[i + 1] = E->x[i];//将最优解复制到v[1:n]
	while(true)
	{
		//释放最小堆中所有结点
		free(E->x);
		DeleteMin(E);
		if(E == NULL)
			break;
	}
	return bestc;
}

int main()
{
	n = 0;
	int i = 0;
	n=5;
	a = (int**)malloc(sizeof(int*) * (n + 1));
	for(i = 1; i <= n; i++)a[i] = (int*)malloc(sizeof(int) * (n + 1));

// 也可以循环读入 
	a[1][1]=0;
	a[1][2]=3;
	a[1][3]=1;
	a[1][4]=5;
	a[1][5]=8;
 
	a[2][1]=3;
	a[2][2]=0;
	a[2][3]=6;
	a[2][4]=7;
	a[2][5]=9;
 
	a[3][1]=1;
	a[3][2]=6;
	a[3][3]=0;
	a[3][4]=4;
	a[3][5]=2;
 
	a[4][1]=5;
	a[4][2]=7;
	a[4][3]=4;
	a[4][4]=0;
	a[4][5]=3;
 
	a[5][1]=8;
	a[5][2]=9;
	a[5][3]=2;
	a[5][4]=3;
	a[5][5]=0;
 
	int*v = (int*)malloc(sizeof(int) * (n + 1));
	for(i = 1; i <= n; i++)
		v[i] = 0;
	bestc = BBTSP(v);
	for(i = 1; i <= n; i++)
	{
		if(i==1) cout<<"路径:";
		if(i!=1) cout<<"->";
		cout<<v[i];
	}
	cout<<endl<< "最短距离"<<bestc<<endl;
	return 0;
}
 

不服来打我!

你可能感兴趣的:(洛谷刷题,ACM)