智能车比赛 【问题描述】 新一届智能车大赛在 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; }