★【视野动态规划】【NOI2011】智能车比赛

智能车比赛
【问题描述】
新一届智能车大赛在 JL 大学开始啦!比赛赛道可以看作是由 n 个矩形区域
拼接而成(如下图所示)
,每个矩形的边都平行于坐标轴,第 i 个矩形区域的左
下角和右上角坐标分别为(xi,1,yi,1)和(xi,2,yi,2)。
题目保证:xi,1<xi,2=xi+1,1,且 yi,1< yi,2,相邻两个矩形一定有重叠在一起的边
(如图中虚线所示)
,智能车可以通过这部分穿梭于矩形区域之间。

选手们需要在最快的时间内让自己设计的智能车从一个给定的起点 S 点到
达一个给定的终点 T 点,
且智能车不能跑出赛道。假定智能车的速度恒为 v 且转
向不消耗任何时间,你能算出最快需要多少时间完成比赛么?
【输入格式】
从文件 car.in 中读入数据。
输入的第一行包含一个正整数 n,表示组成赛道的矩形个数。
接下来 n 行描述这些矩形,
其中第 i 行包含 4 个整数 xi,1, yi,1, xi,2, yi,2,
表示第
i 个矩形左下角和右上角坐标分别为(xi,1, yi,1)和(xi,2, yi,2)。
接下来一行包含两个整数 xS, yS,表示起点坐标。
接下来一行包含两个整数 xT, yT,表示终点坐标。
接下来一行包含一个实数 v(1≤v≤10 ),表示智能车的速度。
【输出格式】
输出到文件 car.out 中。
仅输出一个实数,至少精确到小数点后第六位,为智能车完成比赛的最快时
间。
第4页
共7页
第二十八届全国信息学奥林匹克竞赛
第一试智能车比赛
【评分标准】
对于每个测试点,如果你的输出结果和参考结果相差不超过 10–6,该测试点
得满分,否则不得分。
【样例输入】
2
1 12 2
203 4
1 1
30
1.0
【样例输出】
2.41421356
【数据规模与约定】
约定 所有坐标均为整数 且绝对值不超过 40000所有测试数据的范围和特点如下表所示
测试点编号     n 的规模
1             n≤1
2             n≤5
3
4
5             n≤200
6
7
8             n≤2000
9
10
此题考察视野动态规划。从左到右枚举每一个点,然后向右搜寻,将在视野中的点进行松弛,直到视野为零为止。

一开始没看清楚题,此题所有矩形从左到右依次给出,并且前一个矩形的右界一定是后一个矩形的左界。
Accode:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#define sqr(x) ((x) * (x))

using std::min; using std::max;
const int maxN = 2010;
const double INF = 1e198;

struct vec
{
	int x, y; vec() {}
	vec(int x, int y): x(x), y(y) {}
	vec operator-(const vec &b) const
	{return vec(x - b.x, y - b.y);}
	int operator*(const vec &b) const
	{return x * b.y - y * b.x;}
} S, T;
struct Rec
{
	int x1, x2, y1, y2;
	vec ld() {return vec(x1, y1);}
	vec lu() {return vec(x1, y2);}
	vec rd() {return vec(x2, y1);}
	vec ru() {return vec(x2, y2);}
	bool has(vec p) const
	{return p.x >= x1 && p.x <= x2 && p.y >= y1 && p.y <= y2;}
} rec[maxN]; int n;
double f[maxN << 2], ans = INF, _v;

inline double dist(const vec a, const vec b)
{return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));}

inline bool check(vec lower, vec upper, vec st, vec ed)
{
	if (st.x > ed.x) return 0;
	if ((upper - st) * (ed - st) > 0) return 0;
	if ((lower - st) * (ed - st) < 0) return 0;
	return 1;
}

inline void calc(int Now, vec st, double val)
{
	if (val > 1e197) return;
	vec lower(st.x, st.y - 1), upper(st.x, st.y + 1);
	//定义初始上界为正上方,初始下界为正下方,
	//上下界对应的向量分别为:
	//lower-st以及upper-st。
	for (int i = Now; i < n; ++i)
	{
		if (check(lower, upper, st, rec[i].ld()))
			f[(i << 2) + 0] = min(f[(i << 2) + 0], val 
							+ dist(st, rec[i].ld()));
		if (check(lower, upper, st, rec[i].lu()))
			f[(i << 2) + 1] = min(f[(i << 2) + 1], val 
							+ dist(st, rec[i].lu()));
		if (check(lower, upper, st, rec[i].rd()))
			f[(i << 2) + 2] = min(f[(i << 2) + 2], val 
							+ dist(st, rec[i].rd()));
		if (check(lower, upper, st, rec[i].ru()))
			f[(i << 2) + 3] = min(f[(i << 2) + 3], val 
							+ dist(st, rec[i].ru()));
		if (rec[i].has(T) && check(lower, upper, st, T))
			ans = min(ans, val + dist(st, T));
		//以上几块为更新距离部分,若这些点在视野中则更新。
		if (i < n - 1)
		{
			vec dn(rec[i].x2, max(rec[i].y1, rec[i + 1].y1)),
				up(rec[i].x2, min(rec[i].y2, rec[i + 1].y2));
			if (rec[i].x2 == st.x)
			{
				if (st.y < dn.y || st.y > up.y)
				{
					f[(i << 2) + 4] = min(f[(i << 2) + 4], val 
									+ dist(st, rec[i + 1].ld()));
					f[(i << 2) + 5] = min(f[(i << 2) + 5], val 
									+ dist(st, rec[i + 1].lu()));
					return;
				}
				//这部分是处理起始点在一个矩形的右边界的情况。
			}
			else
			{
				if ((upper - st) * (up - st) < 0) upper = up;
				if ((lower - st) * (dn - st) > 0) lower = dn;
				if ((lower - st) * (upper - st) < 0) return;
				//更新上下界,若上界低于下界则直接退出。
			}
		}
	}
	return;
}

int main()
{
	freopen("car.in", "r", stdin);
	freopen("car.out", "w", stdout);
	scanf("%d", &n);
	for (int i = 0; i < n; ++i)
		scanf("%d%d%d%d", 
			&rec[i].x1, &rec[i].y1, 
			&rec[i].x2, &rec[i].y2);
	scanf("%d%d%d%d%lf", &S.x, &S.y, &T.x, &T.y, &_v);
	if (S.x > T.x) std::swap(S, T);
	for (int i = 0; i < n << 2; ++i) f[i] = INF;
	for (int i = 0; i < n; ++i)
	{
		if (rec[i].has(S)) calc(i, S, 0);
		calc(i, rec[i].ld(), f[(i << 2) + 0]);
		calc(i, rec[i].lu(), f[(i << 2) + 1]);
		calc(i, rec[i].rd(), f[(i << 2) + 2]);
		calc(i, rec[i].ru(), f[(i << 2) + 3]);
		//注意计算顺序,必须严格从左到右计算。
	}
	printf("%.10lf\n", ans / _v);
	return 0;
}



你可能感兴趣的:(struct,测试,UP,2010)