百度之星-Disk Schedule--双调旅行商问题

终于有时间更新了。。。。。

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

分析题目:从起始点开始读取数据,最后还需要回到起始点,求最短时间。

本题目的背景显然是双调旅行商问题。

旅行商问题描述:平面上n个点,确定一条连接各点的最短闭合旅程。这个解的一般形式为NP的(在多项式时间内可以求出)

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

 上图(a)是最短闭合路径,但这个路线不是双调的。(b)是最短双调闭合路线。

求解过程:

 (1)首先将各点按照x坐标从小到大排列,时间复杂度为O(nlgn)。

(2)用b[i][j]来存储从i点到j点的最短路径,即从i到1,再从1到j的路径。dist(i,j)为i到j的位移量。

(3)最优解:我们需要求的是b(n,n)。

分为三个过程:

①当j<i-1时

由双调旅行商的定义可得:i-1必然在i~j的路径上,且i左边的相邻点一定是i-1,因此:

b[i][j]=b[i-1][j]+dist(i-1,i)

②当j=i-1时,1~i-1都有可能是i左边的相邻点,所以需要递归求最短的路径

b[i][j]=min(b[k][j]+dist(i,k)) 1<=k<=j;

③当i=j时,路径上最后可能相邻的点为:1~i,2~i,3~i... ,i-1~i。

所以b[i][i] = min{b[i][1]+dist(1,i),b[i][2]+dist(2,i),........,b[i][i-1]+dist(i-1,i)};

本题需要把时间看成路径,而且设计代码也要根据题目具体要求

详细代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF = 100 * 1000 * 800;
const int MAXN = 1000 + 10;
struct test
{
	int Pathway;//轨道
	int Section;//扇区
};
test vert[MAXN];
int n;
int res[MAXN][MAXN];//存储最短时间
int min(int a, int b)
{
	return a < b ? a : b;
}
//计算两点扇区之间转移需要的最短时间
int dis(int i, int j)
{
	int t = abs(vert[i].Pathway - vert[j].Pathway) * 400;//跳跃轨道需要的时间
	int temp = abs(vert[i].Section - vert[j].Section);
	if (temp>180)
		t += 360 - temp;
	else
		t += temp;
	return t;
}
//动规判断i,j之间最短的时间
void DP()
{
	int i, j;
	res[0][0] = 0;
	res[1][0] = dis(1, 0);
	for (i = 2; i <= n; i++)
	{
		for (j = 0; j < i; j++)
		{
			if (i - 1 != j)//j<i-1的情况
			{
				res[i][j] = res[i - 1][j] + dis(i - 1, i);
			}
			else//j=i-1的情况
			{
				res[i][j] = INF;
				int temp;
				for (int k=0; k < j; k++)
				{
					temp = res[i-1][k] + dis(k, i);
					if (temp < res[i][j])
						res[i][j] = temp;
				}
			}
		}
	}
	int temp;
	res[n][n] = INF;
	//计算从起点去到终点再回到起点的最短路径
	for (i = 0; i < n; i++)
	{
		temp = res[n][i] + dis(n, i);
		if (temp < res[n][n])
			res[n][n] = temp;
	}
}
int main()
{
	int i, j, cas;
	cin >> cas;
	while (cas--)
	{
		scanf("%d", &n);
		vert[0].Pathway = 0;
		vert[0].Section = 0;
		for (i = 1; i <= n; i++)
			scanf("%d%d", &vert[i].Pathway, &vert[i].Section);
		DP();
		printf("%d\n", res[n][n] + n * 10);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值