HDU 4445 扫描线

HDU 4445
题目链接:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=33707
题意:
有区间[L1,R1]和[L2,R2]。
现在站在0点,且高度为h的地方。有n个炮筒,炮筒的炮弹速度不一样,且均已一个自己确定的角度发射。问最多有多少个炮筒,可以打到[L1,R1]区间且所有的炮弹不会打到[L2,R2]区间。
思路:
复现的时候银牌题,对于每个角度每个炮有一个打击范围。然而当时公式推错推成只要是45度角,炮就有最远的打击范围。实际上最远射程是与速度有关系的。当这种式子十分复杂的时候,二分三分就可以上了。有暴力枚举角度(m分角度)过的。

正解是扫描线。
扫描线在大白书第一章第三节有介绍,具体好像都会和一些数据结构结合使用。具体理解就是把一个平面的坐标轴看成以x方向长度为1、y方向无限长的细条。当它在某个值的时候就会覆盖多少多少区间的问题。所以对于每个区间有一个事件数,左端点位进入这个事件,右端点位离开这个事件。
再用人听得懂的话说,一个坐标在左端点和右端点中间的时候,这个区间是有效的。所以把每个区间的左右端点都以两个不同值得形式(通常左端点值为1,表示增加一个区间,右端点值为-1)存入数组,然后进行排序一个个往后遍历即可。
处理的时候如果两个端点坐标相同,根据题目具体意思(到底是开区间还是闭区间)来确定左右端点的优先性。

本题是裸的扫描线。存在[L1,R1]和[L2,R2]区间时两端点角度、不合法区间设值为无限大表示当前角度不合法、合法区间设值为1表示当前合法即可。
注意最远射程在区间中(比如[L1,R1])时,这个点不算数,因为优先级不好确定。有都加进去过的如final队http://blog.csdn.net/ALPC_NeverFarewell/article/details/39397673,原因不详……
源码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
using namespace std;
const int MAXN = 200 + 5;
const double PI = acos(-1.0);
const double eps = 1e-8;
const double g = 9.8;
int n;
double v[MAXN], pos[4], h;
double max_ang[MAXN];
int cnt;
int sgn(double a)
{
    return a > eps ? 1 : (a < -eps ? -1 : 0);
}
double dis(double v, double ang)
{
    double vx = v * cos(ang);
    double vy = v * sin(ang);
    double vt = vy * vy + 2 * g * h;
    vt = max(vt, 0.0);
    double t = (vy + sqrt(vt)) / g;
    return t * vx;
}
void cal_max_ang(int mark)
{
    double l = - PI / 2.0, r = PI / 2.0;
    int cnt = 100;
    while(cnt--){
        double temp = (-l + r) / 3.0;
        if(sgn(dis(v[mark], l + temp) - dis(v[mark], r - temp)) > 0) r = r - temp;
        else    l = temp + l;
    }
    max_ang[mark] = l;
}
struct D
{
    double u;
    int cnt;
    int state;
    void init(double _u, int _state, int _cnt){u = _u, state = _state, cnt = _cnt;}
}d[MAXN * 8];
double dis2(double v, double x, double ang)
{
    double t = x / (v * cos(ang));
    return g / 2 * t * t - v * sin(ang) * t;
}
double cal2(double v, double x, double l, double r)
{
    int cnt = 100;
    while(cnt--){
        double temp = (l + r) / 2.0;
        if(sgn(dis2(v, x, temp) - h) > 0)    r = temp;
        else    l = temp;
    }
    return l;
}
double cal1(double v, double x, double l, double r)
{
    int cnt = 100;
//    printf("x = %f\n", x);
    while(cnt--){
        double temp = (l + r) / 2.0;
//        printf("temp = %f, dis2(v, x, temp) = %f\n", temp, dis2(v, x, temp));
        if(sgn(dis2(v, x, temp) - h) >= 0)    l = temp;
        else    r = temp;
    }
    return l;
}
void solve()
{
    cnt = 0;
    for(int i = 0 ; i < n ; i++){
        cal_max_ang(i);
//        printf("max_ang[%d] = %f\n", i, max_ang[i]);
        double td = dis(v[i], max_ang[i]);
        if(sgn(td - pos[0]) >= 0 && sgn(td - pos[1]) <= 0){
            double ang1 = cal1(v[i], pos[0], -PI / 2 + eps, max_ang[i]);
//            double ang2 = max_ang[i];
            d[cnt++].init(ang1, 0, 1);
//            d[cnt++].init(ang2, 1, -1);
//            ang2 = max_ang[i];
            ang1 = cal2(v[i], pos[0], max_ang[i], PI / 2 - eps);
//            d[cnt++].init(ang2, 0, 1);
            d[cnt++].init(ang1, 1, -1);
        }
        else if(sgn(td - pos[0]) >= 0){
            double ang1 = cal1(v[i], pos[0], -PI / 2 + eps, max_ang[i]);
            double ang2 = cal1(v[i], pos[1], -PI / 2 + eps, max_ang[i]);
            d[cnt++].init(ang1, 0, 1);
            d[cnt++].init(ang2, 1, -1);
            ang1 = cal2(v[i], pos[0], max_ang[i], PI / 2 - eps);
            ang2 = cal2(v[i], pos[1], max_ang[i], PI / 2 - eps);
            d[cnt++].init(ang2, 0, 1);
            d[cnt++].init(ang1, 1, -1);
        }
        if(sgn(td - pos[2]) >= 0 && sgn(td - pos[3]) <= 0){
            double ang1 = cal1(v[i], pos[2], -PI / 2 + eps, max_ang[i]);
            double ang2 = max_ang[i];
            d[cnt++].init(ang1, 0, -998);
//            d[cnt++].init(ang2, 1, 998);
            ang1 = cal2(v[i], pos[2], max_ang[i], PI / 2 - eps);
//            d[cnt++].init(ang2, 0, -998);
            d[cnt++].init(ang1, 1, 998);
        }
        else if(sgn(td - pos[2]) > 0){
            double ang1 = cal1(v[i], pos[2], -PI / 2 + eps, max_ang[i]);
            double ang2 = cal1(v[i], pos[3], -PI / 2 + eps, max_ang[i]);
            d[cnt++].init(ang1, 0, -998);
            d[cnt++].init(ang2, 1, 998);
            ang1 = cal2(v[i], pos[3], max_ang[i], PI / 2 - eps);
            ang2 = cal2(v[i], pos[2], max_ang[i], PI / 2 - eps);
//            if(fabs(ang1 - 1.140593) < 1e-5 || fabs(ang2 - 1.140593) < 1e-5)
//                printf("ang1 = %f ,ang2 = %f\n", ang1, ang2);
            d[cnt++].init(ang1, 0, -998);
            d[cnt++].init(ang2, 1, 998);
        }
//        printf("i = %d, cnt = %d\n", i, cnt);
    }
}
bool cmp(D a, D b)
{
    if(sgn(a.u - b.u) != 0)
        return sgn(a.u - b.u) < 0;
    else if(a.state != b.state)
        return a.state < b.state;
    else{
        if(abs(a.cnt) == -998 || abs(b.cnt) == 998)   return a.cnt < b.cnt;
        else    return a.cnt > b.cnt;
    }
}
int valid1(double ang, double v)
{
    if(dis(v, ang) >= pos[0] && dis(v, ang) <= pos[1])    return 1;
    return 0;
}
int valid2(double ang, double v)
{
    if(dis(v, ang) >= pos[2] && dis(v, ang) <= pos[3]) return 1;
    return 0;
}
int main()
{
//    freopen("HDU 4445.in", "r", stdin);
//    freopen("second.out", "w", stdout);
    while(scanf("%d", &n) != EOF && n){
        scanf("%lf%lf%lf%lf%lf", &h, &pos[0], &pos[1], &pos[2], &pos[3]);
        for(int i = 0 ; i < n ; i++)    scanf("%lf", &v[i]);
        solve();
        sort(d, d + cnt, cmp);
//        for(int i = 0 ; i < cnt ; i++)  printf("d[%d], u = %f, state = %d, cnt = %d\n", i, d[i].u, d[i].state, d[i].cnt);
        int ans = 0;
        int res = 0;
        for(int i = 0 ; i < cnt ; i++){
//            printf("d[i].u = %f, d[i].cnt = %d\n", d[i].u, d[i].cnt);
//            printf("res = %d\n", res);
            res += d[i].cnt;
            ans = max(ans, res);
        }
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值