当前位置: 首页 > news >正文

浅谈旅行商问题(TSP问题)

以POJ3311为例

题目描述:

The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. 
Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait 
for 1 or more (up to 10) orders to be processed before he starts any deliveries. Needless to say, he 
would like to take the shortest route in delivering these goodies and returning to the pizzeria, even if it 
means passing the same location(s) or the pizzeria more than once on the way. He has commissioned 
you to write a program to help him.
输入:
3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0
输出:
8

此题使用一次floyd之后就变成了一道经典的TSP问题.那么下面将浅谈一下TSP问题

对于TSP问题:

指一个旅行商从一个城市触发,经过每一个城市一次且只有一次回到原来的地方,要求经过的距离最短.目前为止TSP问题是一个NP难题,目前并没有多项式时间以内的高效算法,使用动态规划来解决的话,复杂度为 n 2 2 n n^22^n n22n

对于旅行商问题,我们可以使用 d p [ s ] [ u ] dp[s][u] dp[s][u]来表示当前已走过的集合为 s s s并且从 u u u点出发走完所有剩余点并回到出发点的最小距离

那么对于一个点,我们要计算这个点到出发点的距离,显然可以由他的邻接点来进行递推.因为我们的这个点到达终点的路径显然是会经过他的邻接点的.并且这个邻接点的状态方程显然就加入了我们的 v v v这个状态.所以此时我们不难写出以下的转移方程:

d p [ { s } ] [ u ] = m i n ( d p [ { s } ] [ u ] , d p [ { s } ⋃ { v } ] [ v ] + m p [ u ] [ v ] ) dp[\{s\}][u]=min(dp[\{s\}][u],dp[\{s\}\bigcup\{v\}][v]+mp[u][v]) dp[{s}][u]=min(dp[{s}][u],dp[{s}{v}][v]+mp[u][v])

其中mp[u][v]为邻接表中u,v两点直接的距离,为什么直接使用邻接表储存呢.因为TSP问题的复杂度极大,所以实际问题中的点的个数将不会很多,所以邻接表足以,且邻接表更方便使用

那么接下来的问题就是如何存储我们的状态了

对于这个问题,我们将使用状态压缩的一种方式来存储,对于每一种状态,我们都使用一串二进制数来存储,那么对于每一个点假设在我们的点集之中,那就意味着我们的对应的二进制位为1即可

( s > > v ) & 1 (s>>v)\&1 (s>>v)&1 表示从点集s中提取出我们的v点的状态
( s ∣ ( 1 < < v ) ) (s|(1<<v)) (s(1<<v)) 表示将我们的v点的状态加入到我们的点集s中

对于边界问题:

显然我们最终的状态将会是 d p [ ( 1 < < n ) − 1 ] [ 0 ] dp[(1<<n)-1][0] dp[(1<<n)1][0]表示最终全部点都走过并且最终在0点

那么我们最终的答案就是 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]代表刚开始没有走过任何一个点并且从0开始的最短距离

对于我们的TSP问题,接下来我将分别展示递推和记搜两种求解方式

对于递推:

	dp[(1<<n)-1][0]=0;
	for(int s=(1<<n)-2;s>=0;s--) {
		for(int u=0;u<n;u++) {
			for(int v=0;v<n;v++) {
				if((!(s>>u&1)&&!(u==0&&s==0))||mp[u][v]==inf) continue;
				if(!(s>>v&1)) {
					dp[s][u]=min(dp[s][u],dp[s|(1<<v)][v]+mp[u][v]);
				}
			}
		}
	}

也就是枚举每一种状态,然后更新每一种状态中每一个节点的值

对于约束条件部分:

! ( s > > u & 1 ) !(s>>u\&1) !(s>>u&1)表示当前枚举的出发点不在我们的点集当中,那么显然我们此时不应该枚举我们的u节点,但是这个并不是一定的,因为当我们求解我们的dp[0][0]时,最终需要枚举的状态是s==0并且需要从u=0开始出发,所以当我们的s==0并且u==0时,我们需要继续进行递推


对于记忆化搜索,我们有以下代码(感觉记搜比较好理解):

int solve(int s,int u) {//s表示目前走过的点集,u表示目前所在的点的位置
	if(dp[s][u]>=0) return dp[s][u]; 
	if(s==(1<<n)-1&&u==0) return dp[s][u]=0;
	if(s!=(1<<n)-1&&u==0&&s!=0) return dp[s][u]=inf;
	//对于此处剪枝,因为当我们并没有走完全部的点却提前到初始点显然是不合法的
	int ans=inf;
	for(int v=0;v<n;v++) {
		if(!(s>>v&1)&&mp[u][v]!=inf) {
			ans=min(ans,solve(s|(1<<v),v)+mp[u][v]);
		}
	}
	return dp[s][u]=ans;
}
solve(0,0);

相关文章:

  • 怎样做自己的网站和发布网站/优量汇广告平台
  • wordpress中文seo插件/长沙今日头条新闻
  • 莱州网站开发/太原seo快速排名
  • 网上做环评立项的网站是哪个/百度天眼查公司
  • 互联网推广方法/优化网站排名推广
  • python做网站挣钱/抖音关键词用户搜索排名靠前
  • [附源码]JAVA毕业设计口腔医院网站(系统+LW)
  • 急诊医学-急救医学-复习资料-总结-重点-笔记
  • 浏览器高度兼容性
  • DPD(Digital Pre-Distortion,数字预失真)
  • 基于N32G45的按键驱动
  • Springboot旅游餐饮服务平台r1n3j计算机毕业设计-课程设计-期末作业-毕设程序代做
  • 【VSCode + Anaconda】VSCode [WinError 126]找不到指定模块
  • Java数据结构与Java算法学习Day03---线性表(简略笔记记录)
  • 智工教育:注册计量师一级和二级的科目一样吗?
  • 计算机毕业设计Java的音乐网站管理系统(源码+系统+mysql数据库+lw文档)
  • 三、CSS基础-元素显示模式 行元素,块元素,行内块元素
  • Springboot旅游度假村管理系统34q0n计算机毕业设计-课程设计-期末作业-毕设程序代做