Address
https://www.lydsy.com/JudgeOnline/problem.php?id=2433
Solution
一开始看错了题目,卡了好久
我们可以提取出一些关键点,存在一种最优解使得任何拐弯点都为关键点。
容易得到,我们可以从第
个矩形和第
个矩形中提取
个关键点。
它们的横坐标都为
即
,
坐标分别为:
(1)
(2)
下面将上面两式分别记作 和 。
同时我们也能得到,如果终点的 坐标小于起点的 坐标,就将起点和终点互换,如果相等情况下终点的 小于起点的 也互换,那么:
如果起点和终点的 坐标相等,那么最短距离为起点和终点的 坐标之差。
否则任何时候都只会往 坐标更大的方向走。
我们还有一个问题:如何判断是否冲出界线。
也不难得到:
一条 坐标范围为 的线段不越界,当且仅当对于任意 ,线段在 处的 坐标都在 内。
设该线段的起点( 坐标为 的端点)为 。
也就是该线段的斜率大于等于 与 构成直线的斜率,且小于等于 与 构成的斜率。
枚举 后,从左到右枚举终点,期间维护斜率的取值范围,就能求出任意两点是否能够 直线到达。
由于右边的点只能从左边的走到,因此可以用好写的 dp 代替最短路。
设 表示到点 的最短路。
大力 dp 即可求得答案。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 2005, M = 4040, W = 4e6 + 5;
int n, m, lx[N], ly[N], rx[N], ry[N], xS, yS, xT, yT,
L, R, top, ecnt, nxt[W], adj[M], go[W];
double vi, val[W], f[M];
void add_edge(int u, int v, double w) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt;
go[ecnt] = v; val[ecnt] = w;
}
struct cyx {
int x, y, id;
} q[M];
double dist(cyx a, cyx b) {
return sqrt(1.0 * (a.x - b.x) * (a.x - b.x) +
1.0 * (a.y - b.y) * (a.y - b.y));
}
bool comp(cyx a, cyx b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
int main() {
int i, j;
n = read();
For (i, 1, n) lx[i] = read(), ly[i] = read(),
rx[i] = read(), ry[i] = read();
xS = read(); yS = read(); xT = read(); yT = read();
if (xS > xT || (xS == xT && yS > yT))
swap(xS, yS), swap(xT, yT);
cin >> vi;
q[++m] = (cyx) {xS, yS, 1}; q[++m] = (cyx) {xT, yT, 2};
For (i, 1, n - 1)
q[++m] = (cyx) {rx[i], max(ly[i], ly[i + 1]), 3},
q[++m] = (cyx) {rx[i], min(ry[i], ry[i + 1]), 4};
sort(q + 1, q + m + 1, comp);
For (i, 1, m) {
if (q[i].id == 1) L = i;
if (q[i].id == 2) R = i;
}
For (i, L, R) {
double le = -1e20, ri = 1e20;
For (j, i + 1, R) {
if (q[i].x == q[j].x) {
add_edge(j, i, q[j].y - q[i].y);
continue;
}
double slp = 1.0 * (q[j].y - q[i].y) / (q[j].x - q[i].x);
if (le <= slp && slp <= ri)
add_edge(j, i, dist(q[i], q[j]));
if (q[j].id == 3) le = max(le, slp);
else ri = min(ri, slp);
}
}
For (i, L + 1, R) {
f[i] = 1e20;
Edge(i) f[i] = min(f[i], f[v] + val[e]);
}
printf("%.10lf\n", f[R] / vi);
return 0;
}