HDU 4445 扫描线

来源:互联网 发布:怎么查看手机支持网络 编辑:程序博客网 时间:2024/06/07 03:06

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;}
0 0