[BZOJ2433][Noi2011]智能车比赛(计算几何+dp)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=2433

Solution

一开始看错了题目,卡了好久
我们可以提取出一些关键点,存在一种最优解使得任何拐弯点都为关键点。
容易得到,我们可以从第 i 个矩形和第 i + 1 个矩形中提取 2 个关键点。
它们的横坐标都为 x i , 2 x i + 1 , 1 y 坐标分别为:
(1)

max ( y i , 1 , y i + 1 , 1 )

(2)
min ( y i , 2 , y i + 1 , 2 )

下面将上面两式分别记作 l i r i
同时我们也能得到,如果终点的 x 坐标小于起点的 x 坐标,就将起点和终点互换,如果相等情况下终点的 y 小于起点的 y 也互换,那么:
如果起点和终点的 x 坐标相等,那么最短距离为起点和终点的 y 坐标之差。
否则任何时候都只会往 y 坐标更大的方向走。
我们还有一个问题:如何判断是否冲出界线。
也不难得到:
一条 x 坐标范围为 [ a , b ] 的线段不越界,当且仅当对于任意 a x i , 2 b ,线段在 x = x i , 2 处的 y 坐标都在 [ l i , r i ] 内。
设该线段的起点( x 坐标为 a 的端点)为 A
也就是该线段的斜率大于等于 A ( x i , 2 , l i ) 构成直线的斜率,且小于等于 A ( x i , 2 , r i ) 构成的斜率。
枚举 A 后,从左到右枚举终点,期间维护斜率的取值范围,就能求出任意两点是否能够 直线到达
由于右边的点只能从左边的走到,因此可以用好写的 dp 代替最短路。
f [ i ] 表示到点 i 的最短路。
大力 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;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81455849