目录
- 一、旅行商问题简介
 - 旅行商问题
 - 问题概述
 - 问题由来
 
- 二、枚举所有方案
 - 1、思路
 - 2、代码
 - 3、复杂度分析
 
- 三、深度优先搜索
 - 1、思路
 - 2、代码
 - 3、复杂度分析
 
一、旅行商问题简介
旅行商问题
TSP,即旅行商问题,又称TSP问题(Traveling Salesman
Problem),是数学领域中著名问题之一。
问题概述
假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。TSP问题是一个NPC问题。
问题由来
TSP的历史很久,最早的描述是1759年欧拉研究的骑士周游问题,即对于国际象棋棋盘中的64个方格,走访64个方格一次且仅一次,并且最终返回到起始点。
TSP由美国RAND公司于1948年引入,该公司的声誉以及线形规划这一新方法的出现使得TSP成为一个知名且流行的问题。
示例:
 
黑色数字代表点、红色代表路径的花费
输入:
4 6
1 2 1
1 4 2
1 3 4
2 3 1
2 4 2
3 4 3
 
输出:
最短距离为:7
共2条最短路径:
1-->4-->3-->2-->1
1-->2-->3-->4-->1
 
提示:
第一行输入点的个数n和边的个数m,点的编号为1~n
接下来m行输入m条边以及花费,x y v,表示点x和点y之间有一条无向边,边的花费为v
 
二、枚举所有方案
1、思路
  我们只需要固定起点和终点,用全排列枚举中间点的排列,判断方案是否是最优的,如果是,则记录下来
   例如4个点,那么我们固定起点为0,终点为0,枚举1-3的全排列,判断每一种方案是否是最优的,选择性第保留或者抛弃
   4个点的方案有6个:
   0 1 2 3 0
   0 1 3 2 0
   0 2 1 3 0
   0 2 3 1 0
   0 3 1 2 0
   0 3 2 1 0
2、代码
#include<bits/stdc++.h>
using namespace std;
int n,m;//n点的个数,m边的个数 
int a[15][15];//邻接矩阵存无向图 
int p[15];
long long ans=0x3f3f3f3f;
vector<vector<int> > paths;
void init(){//初始化 
	memset(a,0x3f,sizeof a);//初始化为一个足够大的值,代表不可达 
	for(int i=0;i<15;i++){
		p[i]=i;
	}
	cout<<"请输入点和边的个数:"<<endl;
	cin>>n>>m;
	p[0]=0;//起点为0 
	p[n]=0;//终点为0 
	cout<<"请输入"<<m<<"条边:"<<endl;
	for(int i=0;i<m;i++){
		int x,y,val; 
		cin>>x>>y>>val;
		x--;
		y--;
		a[x][y]=val;
		a[y][x]=val;
	}
} 
long long check(){
	long long sum=0;
	for(int i=1;i<=n;i++){
		if(a[p[i-1]][p[i]]==0x3f3f3f3f){//如果该点不可达,那么就直接退出 
			return 0x3f3f3f3f;
		}
		sum+=a[p[i-1]][p[i]];
	}
	return sum;
}
void print(){//打印路径 
	cout<<"最短距离为:"<<ans<<endl;
	cout<<"共"<<paths.size()<<"条最短路径:" <<endl; 
	for(int i=0;i<paths.size();i++){
		for(int j=paths[i].size()-1;j>=0;j--){
			if(j<paths[i].size()-1){
				cout<<"-->";
			}
			cout<<paths[i][j]+1;
		}
		cout<<endl;
	}
}
int main(){
	init();
	do{
		long long sum=check();
		if(sum<=ans){
			if(sum<ans){//如果有更优的解,那么就抛弃掉先去的路径 
				paths.clear();
			}
			ans=sum;
			vector<int> path;
			for(int i=0;i<=n;i++){
				path.push_back(p[i]);
			}
			paths.push_back(path);//添加当前的路径 
		}
	}while(next_permutation(p+1,p+n));//枚举1~n-1的全排列 
	
	if(ans==0x3f3f3f3f){
		cout<<"该图无解"<<endl;
		return 0; 
	}
	print();
}
 
3、复杂度分析
  时间复杂度: 枚举全排列,对每一个排列做一次查询,时间复杂度为O(n*(n!))
   空间复杂度: 存储所有的最优的路径,空间复杂度为O(n*(n!))
三、深度优先搜索
1、思路
我们从0点出发,深度优先搜索所有的路径,符合要求则记录下来
2、代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[15][15];//邻接矩阵存无向图 
long long ans=0x3f3f3f3f;//存储答案
vector<vector<int> > paths;//存储路径 
void init(){//初始化 
	memset(a,0x3f,sizeof a);//初始化为一个足够大的值,代表不可达 
	cout<<"请输入点和边的个数:"<<endl;
	cin>>n>>m;
	cout<<"请输入"<<m<<"条边:"<<endl;
	for(int i=0;i<m;i++){
		int x,y,val; 
		cin>>x>>y>>val;
		x--;
		y--;
		a[x][y]=val;
		a[y][x]=val;
	}
} 
long long sum=0;
vector<int> path(1,0);//存储实时的路径 
int vis[15];//判断该点是否已经走过了 
void dfs(int i){//i指的是目前在哪个点 
	if(i==0){//目前的点是终点
		int flag=1;
		for(int j=0;j<n;j++){//并且所有的点都走过一遍
			if(vis[j]==0)flag=0;
		}
		if(flag==1){
			if(sum<=ans){
				if(sum<ans){//如果有更优的解,则抛弃之前存储的路径 
					paths.clear();
				}
				ans=sum;
				paths.push_back(path);//存储当前路径 
			}
			return;
		}
	}
	for(int j=0;j<n;j++){//深搜+回溯 
		if(vis[j]==0){
			path.push_back(j); 
			sum+=a[i][j];
			vis[j]=1;
			dfs(j);
			path.pop_back();
			sum-=a[i][j];
			vis[j]=0;
		}
	} 
	
}
void print(){//打印路径 
	cout<<"最短距离为:"<<ans<<endl;
	cout<<"共"<<paths.size()<<"条最短路径:" <<endl; 
	for(int i=0;i<paths.size();i++){
		for(int j=paths[i].size()-1;j>=0;j--){
			if(j<paths[i].size()-1){
				cout<<"-->";
			}
			cout<<paths[i][j]+1;
		}
		cout<<endl;
	}
}
int main(){
	init();
	dfs(0);
	if(ans==0x3f3f3f3f){
		cout<<"该图无解"<<endl;
		return 0; 
	}
	print();
}
 
3、复杂度分析
  时间复杂度: 深度搜索O(n!),每次都要判断是否符合题意O(n),总时间复杂度为O(n*(n!))
   空间复杂度: 存储所有的最优的路径,空间复杂度为O(n*(n!))












![[短的文章] Spring Boot 日志创建使用、日志级别、@Slf4j、日志持久化——Spring Boot 系列](https://img-blog.csdnimg.cn/img_convert/13588e5daa7a7d802f61e1a700aaad3d.png)






