回溯法求解TSP问题(旅行商问题)

定义:旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。


本提中对TSP问题,可以看作全排列问题,只不过在排列过程中,只对符合条件的数进行,那怎末判断要去的下一个城市(下一个数是否满足要求,这里就采用了限界(代价)函数,用来减少不必要的循环,也就是所谓的剪枝函数的作用)

这里的限界函数用的是屈婉玲老师的算法与设计分析中提到的方法,
在这里插入图片描述
在这里插入图片描述

代码如下:

#include <stdio.h>
#define max 100
int num ;
int g[max][max], x[max], bestx[max]; //best[]是用来存储最终的最短路径 
int cl=0;
int best = max;

//交换函数 
void swap(int &a, int &b)
{
	int tem = a;
	a= b;
	b =tem;
}


//界定函数,按照屈婉玲老师的算法公开课视频讲解的给出的实现,
//下界公式为 min{cl + min1 + temmin}解释一下就是当前走过的路径+下一个要找的最短路径+剩下点的到其他点的最短路径(最后这个temmin是用贪心的思想求剩下没走过城市的最路径)
//当然,贪心毕竟不是从整体最优考虑,而是从局部最优考虑,所以,在后来的寻找中,如果找到比 cl + min1 + temmin求解出来的最短路径,则可以考虑往下找,否则剪枝。 
int  bound( int t)
{
	int min1=max ,min2=max, temmin=0;  //这里min与min2初始化一下 
	for(int i=t; i<=num; i++)
	{
		//这里求min1 
		if( g[x[t-1]][x[i]] != -1 && g[x[t-1]][x[i]]  < min1)
		{
			min1 =g[x[t-1]][x[i]]; //这里求要去的第t城市。选择当前到t路径最短的 
		}
		
		//这里求min2 
		for(int j =1; j<=num; j++) //贪心的思想,求剩下的路径的下界 
	     {
	     	if( g[x[i]][x[j]]!=-1 &&  g[x[i]][x[j]]!=0  && g[x[i]][x[j]] < min2)
	     	{
	     		min2 = g[x[i]][x[j]];
			 }
		 }
		 temmin += min2; //这里是剩下点的最短出出路径的和的累加 
	}
	return cl+ min1 + temmin;
}

//这里选择城市的方式类似于全排列问题,只不过这里加了 一个判断条件就是t与之前的城市t-1有路径,这里是有选择性的选择点(这里也叫剪枝)。 
void prim(int t)
{
	//如果到叶节点,则表示所有城市都走过了,这个时候就保存一下最短路径以及相应的城市顺序 ,当然是满足条件就是比之前的bext还要小的路径的长度,每次取最优 
	if(t>num )
	{
		if(g[x[num]][1]!=-1 &&  cl + g[x[num]][1] <best) //记得还有最后回到原点 ,这里必须是有到原点的一条路径 
		{
		  for(int i =1; i<=num; i++)
			{
			   bestx[i] =x[i];	 //将走过的顺序放入best数组中 
		    }
			best = cl + g[x[num]][1]; //保存加上回到原点的的路径 
		}
	}
	
	else{
		for(int j =t; j<=num; j++)
		{
			if( g[x[t-1]][x[j]] !=-1 && (bound(t)<best)) //满足条件的选择要选的城市的编号 
			{
				swap(x[j],x[t]);
				cl += g[x[t-1]][x[t]]; //注意,这里不是g[x[t-1]x[j]],j改成t,因为上面的j与t的位置的数换了。 
				
				prim(t+1);
				
				//回溯,恢复走过的。 
				cl -= g[x[t-1]][x[t]];
				swap(x[j],x[t]);
			}
		}
	}
}


int main()
{
	printf("输入城市数: ");
	scanf("%d",&num);
	
	for(int i =1; i<=num; i++)
	{
		x[i] =i; //将城市编号放入一个数组中,初始化 
		bestx[i] = 0;
	}
	
	for(int i =1; i<=num; i++)
	{
		for(int j =1; j<=num; j++)
		{
			scanf("%d",&g[i][j]);
		}
	 } 
	 
	 prim(2); //这里是从第一个城市出发,开始要寻找第二个要走的城市 
	 
	printf("从1号城市出发,经过所有城市回到起点顺序为: "); 
	 for(int i =1; i<=num ;i++)
	 {
         printf("%d --> ", bestx[i]);
	 }
	 
	 if(g[x[num]][1]!=-1);
	 {
	 	printf("%d",bestx[1]); 
	 }
	 
	 printf("\n"); 
	 printf("\n最短路径为:  %d", best); 
	 return 0;
 } 

程序运行如下:

在这里插入图片描述
在这里插入图片描述

  • 18
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
回溯是一种解决旅行问题TSP)的有效方TSP是一个NP难问题,因此回溯是一种可行的解决方案。在这里,我们使用MATLAB来实现回溯解决TSP问题。以下是MATLAB代码: ```matlab function tsp_backtrack(d) % d: 距离矩阵 n = size(d,1); % 城市数量 visited = zeros(1,n); % 访问标记 visited(1) = 1; % 起点已访问 route = 1; % 当前路径 min_dist = Inf; % 最短距离 best_route = []; % 最短路径 tsp_backtrack_helper(d, visited, route, min_dist, best_route, n); disp(['Best route:', num2str(best_route)]); disp(['Minimum distance:', num2str(min_dist)]); function tsp_backtrack_helper(d, visited, route, min_dist, best_route, n) if route == n % 所有城市都已经访问 if d(visited(route),1) < Inf % 如果最后一个城市能够到达起点 dist = sum(d(sub2ind([n,n],visited(1:n-1),visited(2:n)))) + d(visited(n),1); % 计算路径长度 if dist < min_dist % 更新最短路径 min_dist = dist; best_route = visited; end end else for i=1:n % 对于每个未访问的城市 if visited(i) == 0 % 如果该城市未被访问 visited(i) = route+1; % 标记该城市被访问 tsp_backtrack_helper(d, visited, route+1, min_dist, best_route, n); % 递归访问下一个城市 visited(i) = 0; % 恢复未访问状态 end end end ``` 在上面的代码中,我们定义了一个`tsp_backtrack`函数,它接受一个距离矩阵作为输入。我们首先初始化访问标记和起点,然后调用`tsp_backtrack_helper`函数来计算最短路径。在`tsp_backtrack_helper`函数中,我们使用递归来访问每个未访问的城市。如果我们访问了所有城市,我们会检查最后一个城市是否能够回到起点。如果是这样,我们计算路径长度并更新最短路径和最短距离。最后,我们打印最短路径和最短距离。 要测试这个算,我们可以使用以下代码: ```matlab % 设置距离矩阵 d = [Inf 20 30 10; 15 Inf 16 4; 3 5 Inf 2; 19 6 18 Inf]; % 调用回溯 tsp_backtrack(d); ``` 输出结果应该如下: ``` Best route: 1 4 2 3 Minimum distance: 25 ``` 这意味着最短路径是1-4-2-3-1,路径长度为25。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值