bzoj1038: [ZJOI2008]瞭望塔

半平面交

过点(xi-1,yi-1)到点(xi,yi)作直线,直线上方是可选区域

然后求半平面交

最小高度只可能在所求半平面交的顶点或山的轮廓顶点处取到

用扫描线维护一下就行了

复杂度O(nlogn),不过此题n很小,暴力O(n^2)也能过

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
#define MAXN 303
#define EPS (1e-6)
int dcmp(double a, double b = 0)
{
    if (fabs(a - b) <= EPS)
        return 0;
    return a < b ? -1 : 1;
}
struct Point
{
    double x, y;
    Point(double _x = 0, double _y = 0)
        : x(_x), y(_y) {}
} a[MAXN];
typedef Point Vector;
Vector operator- (const Vector &a, const Vector &b)
{
    return Vector(a.x - b.x, a.y - b.y);
}
double cross(const Vector &a, const Vector &b)
{
    return a.x * b.y - a.y * b.x;
}
bool parallel(const Vector &a, const Vector &b)
{
    return dcmp(a.x * b.y, a.y * b.x) == 0;
}
struct Line
{
    Point p;
    Vector v; // left
    Line() {}
    Line(const Point &_p, const Vector &_v)
        : p(_p), v(_v) {}
    bool operator< (const Line &x)const
    {
        return v.y / v.x < x.v.y / x.v.x;
    }
} l[MAXN], l2[MAXN];
Point intersect(const Line &a, const Line &b)
{
    double y = (a.v.x * a.p.y * b.v.y - a.p.x * b.v.y * a.v.y + b.p.x * b.v.y * a.v.y - b.p.y * b.v.x * a.v.y) / (a.v.x * b.v.y - b.v.x * a.v.y);
    double x;
    if (dcmp(a.v.y))
        x = a.v.x / a.v.y * (y - a.p.y) + a.p.x;
    else
        x = b.v.x / b.v.y * (y - b.p.y) + b.p.x;
    return Point(x, y);
}
int n;
typedef struct ptrbase *ptr;
struct ptrbase
{
    int data;
    ptr next, last;
    ptrbase(int _data, ptr _next = NULL, ptr _last = NULL)
        : data(_data), next(_next), last(_last) {}
};
ptr head, tail;
bool InHalf(const Point &p, const Line &l)
{
    return dcmp(cross(l.v, p - l.p)) >= 0;
}
struct Event
{
    int type; // 0--up 1--down
    int num;
    Event(int _type, int _num)
        : type(_type), num(_num) {}
    bool operator< (const Event &x)const
    {
        double x1 = type ? a[num].x : intersect(l2[num], l2[num + 1]).x;
        double x2 = x.type ? a[x.num].x : intersect(l2[x.num], l2[x.num + 1]).x;
        return x1 < x2;
    }
};
vector<Event> e;
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
        scanf("%lf", &a[i].x);
    for (int i = 0; i < n; ++i)
        scanf("%lf", &a[i].y);
    double mm = a[0].y;
    for (int i = 1; i < n; ++i)
        mm = max(mm, a[i].y);
    for (int i = 1; i < n; ++i)
        l[i - 1] = Line(a[i - 1], a[i] - a[i - 1]);
    sort(l, l + n - 1);
    head = tail = new ptrbase(0);
    for (int i = 1; i < n - 1; ++i)
    {
        if (tail && parallel(l[tail->data].v, l[i].v))
        {
            if (InHalf(l[i].p, l[tail->data]))
            {
                ptr p = tail->last;
                delete tail;
                tail = p;
                if (tail)
                    tail->next = NULL;
                else
                    head = NULL;
            }
            else
                continue;
        }
        while (tail && tail->last)
            if (InHalf(intersect(l[tail->data], l[tail->last->data]), l[i]))
                break;
            else
            {
                tail = tail->last;
                delete tail->next;
                tail->next = NULL;
            }
        while (head && head->next)
            if (InHalf(intersect(l[head->data], l[head->next->data]), l[i]))
                break;
            else
            {
                head = head->next;
                delete head->last;
                head->last = NULL;
            }
        if (head == NULL)
            head = tail = new ptrbase(i);
        else
        {
            tail->next = new ptrbase(i, NULL, tail);
            tail = tail->next;
        }
    }
    int m = 0;
    for (ptr i = head; i; i = i->next)
        l2[m++] = l[i->data];
    for (int i = 0; i < n; ++i)
        e.push_back(Event(1, i));
    for (int i = 0; i < m - 1; ++i)
        e.push_back(Event(0, i));
    sort(e.begin(), e.end());
    double ans = 1e300;
    vector<Event>::iterator s = e.begin();
    while (s != e.end() && s->type == 0)
        ++s;
    vector<Event>::iterator t = e.end();
    while (t != e.begin() && (t - 1)->type == 0)
        --t;
    int i = 0, j = 0; // i--up j--down
    for (vector<Event>::iterator it = e.begin(); it != e.end(); ++it)
    {
        if (it->type == 0)
        {
            if (it >= s && it < t)
            {
                Point p = intersect(l2[i], l2[i + 1]);
                ans = min(ans, p.y - (a[j - 1].y + (p.x - a[j - 1].x) * (a[j].y - a[j - 1].y) / (a[j].x - a[j - 1].x)));
            }
            ++i;
        }
        else
        {
            if (it >= s && it < t)
                ans = min(ans, l2[i].p.y + (a[j].x - l2[i].p.x) * l2[i].v.y / l2[i].v.x - a[j].y);
            ++j;
        }
    }
    printf("%.3lf\n", ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值