• c++旅行商问题 (暴力解)


    一、旅行商问题简介

    旅行商问题

      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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出:

    最短距离为:72条最短路径:
    1-->4-->3-->2-->1
    1-->2-->3-->4-->1
    
    • 1
    • 2
    • 3
    • 4

    提示:

    第一行输入点的个数n和边的个数m,点的编号为1~n
    接下来m行输入m条边以及花费,x y v,表示点x和点y之间有一条无向边,边的花费为v
    
    • 1
    • 2

    二、枚举所有方案

    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
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    3、复杂度分析

      时间复杂度: 枚举全排列,对每一个排列做一次查询,时间复杂度为O(n*(n!))
      空间复杂度: 存储所有的最优的路径,空间复杂度为O(n*(n!))

    三、深度优先搜索

    1、思路

      我们从0点出发,深度优先搜索所有的路径,符合要求则记录下来

    2、代码

    #include
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    3、复杂度分析

      时间复杂度: 深度搜索O(n!),每次都要判断是否符合题意O(n),总时间复杂度为O(n*(n!))
      空间复杂度: 存储所有的最优的路径,空间复杂度为O(n*(n!))

  • 相关阅读:
    electron前端开启局域网接口
    11、Service访问Pod、Service IP原理、DNS访问Service、外部访问service
    基于JSP和MySQL实现的易买网电商网站设计
    pytorch的安装【全官网流程】
    厨卫家居企业应该开展那些线上营销如何开展?
    16. Seata 分布式事务
    【Linux】管道
    Unity 场景烘培 ——unity Post-Processing后处理1(四)
    移动安全测试框架-MobSF WINDOWS 环境搭建
    分享96个节日庆典PPT,总有一款适合您
  • 原文地址:https://blog.csdn.net/weixin_52115456/article/details/127933238