双调TSP问题通俗讲解

欧几里得旅行商问题是对平面上给定的n个点确定一条连接各点的最短闭合旅程的问题,下图a给出了7个点问题的解。这个问题的一般形式是NP完全的,故其解需要多于多项式的时间。

                 

                     (a)                                                          (b)

     J.L.Bentley建议通过只考虑双调旅程来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。b显示了同样7个点问题的最短双调路线。在这种情况下,多项式时间的算法是可能的。

   描述一个确定最优双调路线的O(n^2)时间的算法,可以假设任何两点的x坐标都不相同。

 

 

将各个节点从左到右排序,编号为1,2,3,.....,n。对于任意的i和j(其中1<=i,j<=n)。

现在有两条路径A、B都是从点1出发,A走的路径为1~ i ,B走的路径为1~ j ,

即A、B有公共的起点,但途中没有交叉点(即终点之前不存在 i=j ),终点可能重合(i=j),也可能不重合(i≠j),这取决与我们要求的问题。

令s=max(i,j),则从1到s所有的点一定在路径A或者路径B上,不会有遗漏的点.

对于特定的 i 和 j ,路径A、B存在多种可能的走法,其中比有一种2条路径的和最小的走法,我们把这2条路径的和记为b[i,j];当i=j时,b[i,j]表示了从1到 i 的双调TSP的解;当i=j=n时 b[i,j] 就表示了整个问题的最终解法。

我们可以采用DP(动态规划)求解

上图表示对b[i,j]的递推情况,开始时,显然b[i,j]=0,而i=0或j=0也可以直接确定b[i,j]的值;

基于对称的考虑,表的左下半部和右上半部的数值将完全相同,所以在生成表的时候可以不用考虑下半部分

如何求递推?分一下几种情况

① i > j (即图的右上部分)

已知b[i,j] ,求b[i+1,j],只要将A直接延长到 i+1就行。

b[i+1,j] = b[i,j] + distance(i,i+1)

 

i = j (对角线部分)

假设已知b[i,i],求b[i+1,i],可以想象,此时AB两条路径在终点 i 相交,因为现在我们要求A的终点为i+1,所以不得不把相交的AB在i点拆开。

 

事情没那么简单,拆开后,我们需要在路径A找任意点u(0<=u<=i),使点u连接点i+1.

我们需要找到 min{ b[u,j] + distance(u, i+1)},即遍历 b[1..i, j],刚好这是图的左下部分,因为这个部分是和右上部分对称的,所以可以直接利用右上部分的数据。

b[i+1,j] = min{ B[u,i] + w(u,i+1) }

③ 确定b[n,n],即确定最终的解。

前面 ①②部分已经可以把所有的点都走一次了,现在我们最后需要做的是,把AB路径的头连接起来

那怎么连接才能让AB路径加起来最短呢?

前面我们说过: s=max(i,j),则从1到s所有的点一定在路径A或者路径B上,不会有遗漏的点

通过①②我们已经求出 1~s 点的所有数据。

也就是我们可以通过 min{ b[n,k] + distance(n,k)} 其中 1<=k<n 求出b[n,n];

b[n,n] =  min{ b[n,k] + distance(n,k)}

 

总结一下:

 b[i,j] = b[i-1,j] + distance(i-1,i) (j+1 < i ,蓝色部分)

 b[i,j] = min{ B[u,i] + w(u,i+1) } (j+1 = i ,红色部分)

b[n,n] =  min{ b[n,k] + distance(n,k)}  0<=k<n

 

下面以2014百度之星Disk Schedule为例子,使用TSP算法

Problem Description

有很多从磁盘读取数据的需求,包括顺序读取、随机读取。为了提高效率,需要人为安排磁盘读取。然而,在现实中,这种做法很复杂。我们考虑一个相对简单的场景。
磁盘有许多轨道,每个轨道有许多扇区,用于存储数据。当我们想在特定扇区来读取数据时,磁头需要跳转到特定的轨道、具体扇区进行读取操作。为了简单,我们假设磁头可以在某个轨道顺时针或逆时针匀速旋转,旋转一周的时间是360个单位时间。磁头也可以随意移动到某个轨道进行读取,每跳转到一个相邻轨道的时间为400个单位时间,跳转前后磁头所在扇区位置不变。一次读取数据的时间为10个单位时间,读取前后磁头所在的扇区位置不变。磁头同时只能做一件事:跳转轨道,旋转或读取。
现在,需要在磁盘读取一组数据,假设每个轨道至多有一个读取请求,这个读取的扇区是轨道上分布在 0到359内的一个整数点扇区,即轨道的某个360等分点。磁头的起始点在0轨道0扇区,此时没有数据读取。在完成所有读取后,磁头需要回到0轨道0扇区的始点位置。请问完成给定的读取所需的最小时间。

Input

输入的第一行包含一个整数M(0<M<=100),表示测试数据的组数。
对于每组测试数据,第一行包含一个整数N(0<N<=1000),表示要读取的数据的数量。之后每行包含两个整数T和S(0<T<=1000,0<= S<360),表示每个数据的磁道和扇区,磁道是按升序排列,并且没有重复。

Output

对于每组测试数据,输出一个整数,表示完成全部读取所需的时间。

Sample Input

3

1

1 10

3

1 20

3 30

5 10

2

1 10

2 11

Sample Output

830

4090

1642

 我们把要读取的数据的位置定义为 坐标(T,S),化成坐标后,我们需要对T的大小进行排序。

然后按照TSP的思路进行求解即刻。

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define INF 88888888888
struct Data {
	int T, S;
};

int b[1001][1001];
Data p[1001];
int N;

int distance(Data x, Data y) {
	int dis;
	dis = abs(x.T - y.T) * 400;

	int tmp = abs(x.S - y.S);
	if (tmp > 180)dis += 360 - tmp;
	else dis += tmp;
	return dis;
}

bool cmp(Data x, Data y) {
	return x.T < y.T;
}

void DP() {
	int i, j, k;

	b[0][0] = 0;
	b[1][0] = distance(p[1], p[0]);

	for (i = 2; i <= N; i++) {
		for (j = 0; j < i; j++) {
			if (i != j + 1)
				b[i][j] = b[i - 1][j] + distance(p[i - 1], p[i]);
			else {
				b[i][j] = INF;
				int temp;
				for (k = 0; k < j; k++) {
					temp = b[i - 1][k] + distance(p[k], p[i]);
					if (temp < b[i][j])
						b[i][j] = temp;
				}
			}
		}
	}

	//求b[n][n]
	b[N][N] = INF;
	int temp;
	for (k = 0; k < N; k++) {
		temp = b[N][k] + distance(p[N], p[k]);
		if (temp < b[N][N])
			b[N][N] = temp;
	}
}

int main() {
	int M;
	cin >> M;
	while (M--) {
		cin >> N;
		p[0].T = 0;
		p[0].S = 0;
		for (int i = 1; i <= N; i++) {
			cin >> p[i].T >> p[i].S;
		}
		sort(p, p + N + 1, cmp);
		DP();
		cout << b[N][N] + N * 10 << endl;
	}
	return 0;
}

 

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值