[ICPC2020南京I] Interested in Skiing - dp,计算几何

Description

二维平面上有若干条线段和两条直线 x=-m, x=m 作为障碍,从 \((0,-\infty)\) 走到 \((0,\infty)\),不能碰到任何障碍。\(y\) 方向速度恒定为 \(+v_y\)\(x\) 方向速度可以随意调节,但其绝对值有一个上界 \(a\)。求最小的 \(a\) 是多少。

Solution

最优策略一定是经过若干条线段的端点,因此拿着所有端点做个 dp 即可,过程中需要用到判交,暴力判断即可,总体时间复杂度 \(O(n^3)\)

#include <bits/stdc++.h>
using namespace std;

namespace geo
{

#define mp make_pair
#define fi first
#define se second
#define pb push_back
    typedef double db;
    const db eps = 1e-6;
    const db pi = acos(-1);
    int sign(db k)
    {
        if (k > eps)
            return 1;
        else if (k < -eps)
            return -1;
        return 0;
    }
    int cmp(db p1, db p2) { return sign(p1 - p2); }
    int inmid(db p1, db p2, db p3) { return sign(p1 - p3) * sign(p2 - p3) <= 0; }

    struct point
    {
        db x, y;
        point operator+(const point &p1) const { return (point){p1.x + x, p1.y + y}; }
        point operator-(const point &p1) const { return (point){x - p1.x, y - p1.y}; }
        point operator*(db p1) const { return (point){x * p1, y * p1}; }
        point operator/(db p1) const { return (point){x / p1, y / p1}; }
        int operator==(const point &p1) const { return cmp(x, p1.x) == 0 && cmp(y, p1.y) == 0; }
        // 逆时针旋转
        point turn(db p1) { return (point){x * cos(p1) - y * sin(p1), x * sin(p1) + y * cos(p1)}; }
        point turn90() { return (point){-y, x}; }
        bool operator<(const point p1) const
        {
            int a = cmp(x, p1.x);
            if (a == -1)
                return 1;
            else if (a == 1)
                return 0;
            else
                return cmp(y, p1.y) == -1;
        }
        db abs() { return sqrt(x * x + y * y); }
        db abs2() { return x * x + y * y; }
        db dis(point p1) { return ((*this) - p1).abs(); }
        point unit()
        {
            db w = abs();
            return (point){x / w, y / w};
        }
        void scan()
        {
            double p1, p2;
            cin >> p1 >> p2;
            x = p1;
            y = p2;
        }
        void print() { printf("%.11lf %.11lf\n", x, y); }
        db getw() { return atan2(y, x); }
        point getdel()
        {
            if (sign(x) == -1 || (sign(x) == 0 && sign(y) == -1))
                return (*this) * (-1);
            else
                return (*this);
        }
        int getP() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) == -1); }
    };
    int inmid(point p1, point p2, point p3) { return inmid(p1.x, p2.x, p3.x) && inmid(p1.y, p2.y, p3.y); }
    db cross(point p1, point p2) { return p1.x * p2.y - p1.y * p2.x; }
    db dot(point p1, point p2) { return p1.x * p2.x + p1.y * p2.y; }
    db rad(point p1, point p2) { return atan2(cross(p1, p2), dot(p1, p2)); }
    // -pi -> pi
    int compareangle(point p1, point p2)
    {
        return p1.getP() < p2.getP() || (p1.getP() == p2.getP() && sign(cross(p1, p2)) > 0);
    }
    point proj(point p1, point p2, point q)
    { // q 到直线 p1,p2 的投影
        point k = p2 - p1;
        return p1 + k * (dot(q - p1, k) / k.abs2());
    }
    point reflect(point p1, point p2, point q) { return proj(p1, p2, q) * 2 - q; }
    int clockwise(point p1, point p2, point p3)
    { // p1 p2 p3 逆时针 1 顺时针 -1 否则 0
        return sign(cross(p2 - p1, p3 - p1));
    }
    int checkLL(point p1, point p2, point p3, point p4)
    { // 求直线 (L) 线段 (S)p1,p2 和 p3,p4 的交点
        return cmp(cross(p3 - p1, p4 - p1), cross(p3 - p2, p4 - p2)) != 0;
    }
    point getLL(point p1, point p2, point p3, point p4)
    {
        db w1 = cross(p1 - p3, p4 - p3), w2 = cross(p4 - p3, p2 - p3);
        return (p1 * w2 + p2 * w1) / (w1 + w2);
    }
    int intersect(db l1, db r1, db l2, db r2)
    {
        if (l1 > r1)
            swap(l1, r1);
        if (l2 > r2)
            swap(l2, r2);
        return cmp(r1, l2) == 1 && cmp(r2, l1) == 1;
    }
    int checkSS(point p1, point p2, point p3, point p4)
    {
        return intersect(p1.x, p2.x, p3.x, p4.x) && intersect(p1.y, p2.y, p3.y, p4.y) &&
               sign(cross(p3 - p1, p4 - p1)) * sign(cross(p3 - p2, p4 - p2)) < 0 &&
               sign(cross(p1 - p3, p2 - p3)) * sign(cross(p1 - p4, p2 - p4)) < 0;
    }
    db disSP(point p1, point p2, point q)
    {
        point p3 = proj(p1, p2, q);
        if (inmid(p1, p2, p3))
            return q.dis(p3);
        else
            return min(q.dis(p1), q.dis(p2));
    }
    db disSS(point p1, point p2, point p3, point p4)
    {
        if (checkSS(p1, p2, p3, p4))
            return 0;
        else
            return min(min(disSP(p1, p2, p3), disSP(p1, p2, p4)), min(disSP(p3, p4, p1), disSP(p3, p4, p2)));
    }
    int onS(point p1, point p2, point q) { return inmid(p1, p2, q) && sign(cross(p1 - q, p2 - p1)) == 0; }
    struct line
    {
        // p[0]->p[1]
        point p[2];
        line(point p1 = {0, 0}, point p2 = {0, 0})
        {
            p[0] = p1;
            p[1] = p2;
        }
        point &operator[](int k) { return p[k]; }
        int include(point k) { return sign(cross(p[1] - p[0], k - p[0])) > 0; }
        point dir() { return p[1] - p[0]; }
        line push()
        { // 向外 ( 左手边 ) 平移 eps
            const db eps = 1e-6;
            point delta = (p[1] - p[0]).turn90().unit() * eps;
            return {p[0] - delta, p[1] - delta};
        }
    };
    point getLL(line p1, line p2) { return getLL(p1[0], p1[1], p2[0], p2[1]); }
    int parallel(line p1, line p2) { return sign(cross(p1.dir(), p2.dir())) == 0; }
    int sameDir(line p1, line p2) { return parallel(p1, p2) && sign(dot(p1.dir(), p2.dir())) == 1; }
    int operator<(line p1, line p2)
    {
        if (sameDir(p1, p2))
            return p2.include(p1[0]);
        return compareangle(p1.dir(), p2.dir());
    }
    int checkpos(line p1, line p2, line p3) { return p3.include(getLL(p1, p2)); }

}

typedef geo::point vec2;
typedef geo::line line;
using geo::checkSS;

bool cmp(const vec2 &lhs, const vec2 &rhs)
{
    return lhs.y < rhs.y;
}

signed main()
{
    ios::sync_with_stdio(false);
    int n, m;
    double vy;
    cin >> n >> m >> vy;
    vector<line> l(n + 2);
    for (int i = 1; i <= n; i++)
        l[i].p[0].scan(), l[i].p[1].scan();
    vector<vec2> p(2 * n + 2);
    p[0] = {0, -1e100};
    for (int i = 1; i <= n; i++)
        p[2 * i - 1] = l[i].p[0], p[2 * i] = l[i].p[1];
    p[2 * n + 1] = {0, 1e100};
    sort(p.begin(), p.end(), cmp);
    vector<double> f(2 * n + 2);
    for (int i = 1; i <= 2 * n + 1; i++)
    {
        f[i] = 1e100;
        if (abs(p[i].x) >= m)
            continue;
        for (int j = 0; j < i; j++)
        {
            if (p[i].y == p[j].y)
                continue;
            int flag = 1;
            for (int k = 1; k <= n; k++)
            {
                if (checkSS(l[k].p[0], l[k].p[1], p[j], p[i]))
                {
                    flag = 0;
                    break;
                }
            }
            if (flag)
            {
                f[i] = min(f[i], max(f[j], abs(p[i].x - p[j].x) / (p[i].y - p[j].y + 1e-18)));
            }
        }
    }
    if (f[2 * n + 1] <= 1e20)
        cout << fixed << setprecision(16) << f[2 * n + 1] * vy << endl;
    else
        cout << -1 << endl;
}

相关文章: