【算法每日一练]-旅行商(保姆级教程 篇1)

题目:TSP旅行商

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

输入: 输出:22

5 8

0 1 3

0 3 4

1 2 5

2 0 4

2 3 5

3 4 3

4 0 7

4 1 6

思路:

如果采用搜索+剪枝,算法复杂度为O(n!)。

而这种情况下恰好可以考虑状态压缩,常用的状态压缩为二进制,对应0和1两种状态

(当然如果有三种状态的话就要使用3进制,哦不对我们应该用4进制,因为4进制比3进制更快一点)

状态压缩:将集合作为整数来记录状态的算法

基于状压的dp是一类以集合信息为状态且状态总数为指数级的特殊的动态规划问题

我们设置dp[S][u]表示已经访问的集合为S,当前所在的节点为u所对应的最优解

dp[S][u]=min{dp[S|{v}][v]+g[u][v] }(v不属于S)

因为dp[S][u]的下个状态就是dp[S|{v}][v]

下面给出记忆化dfs写法,一般来讲记忆化dfs比动态规划要快一点,因为动态规划往往会在计算无意义的状态中浪费时间。

cpp 复制代码
#include <bits/stdc++.h>//TSP旅行商问题
using namespace std;
int dp[1<<15][20],g[15][15],path[1<<15][15];//path为路径
int n,m,INF;

void init(){
	memset(dp,-1,sizeof(dp));
	memset(g,0x3f,sizeof(g));
	INF=g[0][0];
}

int run(int s,int u){
	if(dp[s][u]>=0) return dp[s][u];
	if(s==(1<<n)-1&&u==0) return dp[s][u]=0;
	int ans=INF;
	for(int v=0;v<n;v++)
		if(!(s>>v&1)&&g[u][v]!=INF){
			int tmp=run(s|1<<v,v)+g[u][v];
			if(ans>tmp){
				ans=tmp;
				path[s][u]=v;//保存后继(如果不存储集合的话)
			}
		//	ans=min(ans,run(s|1<<v,v)+g[u][v]);不输出路径的写法
		}
	return dp[s][u]=ans;
}
void print(int s,int u){
	if(s==(1<<n)-1)return;//这是倒序输出的写法,先找到终态然后倒着输出。如果我们存的是前驱,那就要正序输出了
	int v=path[s][u];
	cout<<"->"<<v;
	print(s|1<<v,v);
}
int main(){
	int u,v,w;
	cin>>n>>m;
	init();
	for(int i=0;i<m;i++){
		cin>>u>>v>>w;
		g[u][v]=w;
	}
	run(0,0);
	cout<<dp[0][0]<<'\n';
	cout<<0;
	print(0,0);//输出路径
	return 0;
}

题目:吃馅饼POJ3311

题意:一个人从0开始送外卖,每次送外卖不超过10个地方,给你两两之间所需的时间,求送完外卖回到店里的总时间最小,每个地方可以经历任意次

输入:(0表示结束) 输出:8

3

0 1 10 10

1 0 1 2

10 1 0 10

10 2 10 0

0

思路:

因为每个点可以走很多次,那么最好就是先把两点间最短距离写出(floyd),然后就转化成了旅行商问题

因为我们要把所有点都走至少一次,那么当旅行商问题可以解出答案时,就一定是最佳答案

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int M=19,INF=0x3f3f3f3f;
int dp[1<<11][M],g[M][M],path[1<<15][15];//path为路径
int n;

void init(){
	memset(dp,-1,sizeof(dp));
	memset(g,0x3f,sizeof(g));
}

void floyd(){
	for(int k=0;k<n;k++)
		for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
int TSP(int s,int u){
	if(dp[s][u]>=0) return dp[s][u];
	if(s==(1<<n)-1&&u==0) return dp[s][u]=0;
	int ans=INF;
	for(int v=0;v<n;v++)
		if(!(s>>v&1)&&g[u][v]!=INF){
			int tmp=TSP(s|1<<v,v)+g[u][v];
			if(ans>tmp){
				ans=tmp;
				path[s][u]=v;//保存后继(如果不存储集合的话)
			}
			//ans=min(ans,run(s|1<<v,v)+g[u][v]);//不输出路径的写法
		}
	return dp[s][u]=ans;
}
void print(int s,int u){
	if(s==(1<<n)-1)return;//这是倒序输出的写法,先找到终态然后倒着输出。如果我们存的是前驱,那就要正序输出了
	int v=path[s][u];
	cout<<"->"<<v;
	print(s|1<<v,v);
}
int main(){
	while(scanf("%d",&n)==1&&n){
		n++;//加原点
		init();
		for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			scanf("%d",&g[i][j]);
		floyd();
		memset(dp,-1,sizeof(dp));
		printf("%d\n",TSP(0,0));
		cout<<0;
		print(0,0);//输出路径
	}
	return 0;
}
相关推荐
Ocean☾2 分钟前
前端基础-html-注册界面
前端·算法·html
顶呱呱程序10 分钟前
2-143 基于matlab-GUI的脉冲响应不变法实现音频滤波功能
算法·matlab·音视频·matlab-gui·音频滤波·脉冲响应不变法
爱吃生蚝的于勒32 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~35 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
王哈哈^_^1 小时前
【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·pyqt
星沁城1 小时前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
脉牛杂德1 小时前
多项式加法——C语言
数据结构·c++·算法
legend_jz1 小时前
STL--哈希
c++·算法·哈希算法
CSUC1 小时前
【C++】父类参数有默认值时子类构造函数列表中可以省略该参数
c++
Vanranrr2 小时前
C++ QT
java·c++·qt