[HDU 4445 Crazy Tank] 三分最远角度+二分命中区间+前缀和求最大值

题目

http://acm.hdu.edu.cn/showproblem.php?pid=4445

分析

三分最远角度+二分命中区间+前缀和求最大值

对于每个炮弹,三分出最远距离的角度,然后在这个角度的两侧,分别有一个区间可以命中对应的目标

利用二分求出在两侧对应的区间

对于敌方目标的区间左端点事件点+1,右端点事件点-1

对于我方目标的区间左断点事件点-INF,右端点事件点+INF

同一个炮弹的敌方目标区间只计算一次,对事件点求前缀和,最大值即为答案

训练赛时只想到了后面二分和前缀和的部分,没想到前面对每个炮弹三分出最远的角度

代码

/**************************************************
 *        Problem:  HDU 4445
 *         Author:  clavichord93
 *          State:  Accepted
 **************************************************/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define sqr(a) ((a) * (a))
using namespace std;
const double EPS = 1e-9;
const double PI = acos(-1.0);
const double DINF = 1e30;
const int INF = 100000;

inline int sgn(double a) {
    return a > EPS ? 1 : (a < -EPS ?-1 : 0);
}

const int MAX_N = 205;
const double g = 9.8;

struct Event {
    double ang;
    int value, id;
    Event() {}
    Event(double _ang, int _value, int _id) : ang(_ang), value(_value), id(_id) {}
    bool operator < (const Event &a) const {
        return sgn(ang - a.ang) < 0;
    }
};

double H, L1, R1, L2, R2;
int n;
int cntAng;
double v[MAX_N];
Event ang[MAX_N * 8];
int cnt[MAX_N];

double f(double v, double ang) {
    double A = g / (2.0 * sqr(v) * sqr(cos(ang)));
    double B = -tan(ang);
    double C = -H;
    double delta = B * B - 4.0 * A * C;
    return (-B + sqrt(delta)) / (2.0 * A);
}

double find1(double l, double r, double limit, double v) {
    double ans = r;
    while (l <= r) {
        double mid = 0.5 * (l + r);
        double x = f(v, mid);
        if (sgn(x - limit) >= 0) {
            ans = mid;
            r = mid - EPS;
        }
        else {
            l = mid + EPS;
        }
    }
    return ans;
}

double find2(double l, double r, double limit, double v) {
    double ans = l;
    while (l <= r) {
        double mid = 0.5 * (l + r);
        double x = f(v, mid);
        if (sgn(x - limit) <= 0) {
            ans = mid;
            r = mid - EPS;
        }
        else {
            l = mid + EPS;
        }
    }
    return ans;
}

int main() {
    #ifdef LOCAL_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    #endif
    while (scanf("%d", &n), n) {
        memset(cnt, 0, sizeof(cnt));

        scanf("%lf %lf %lf %lf %lf", &H, &L1, &R1, &L2, &R2);
        for (int i = 0; i < n; i++) {
            scanf("%lf", &v[i]);
        }

        cntAng = 0;
        for (int i = 0; i < n; i++) {
            double l = -0.5 * PI + EPS, r = 0.5 * PI - EPS;
            double ans = -0.5 * PI;
            while (l <= r) {
                double length = (r - l) / 3.0;
                double x1 = l + length;
                double x2 = l + 2.0 * length;
                double f1 = f(v[i], x1); 
                double f2 = f(v[i], x2);
                //printf("f[%.10f] = %.10f, f[%.10f] = %.10f\n", x1, f1, x2, f2);
                if (sgn(f1 - f2) >= 0) {
                    ans = x1;
                    r = x2 - EPS;
                }
                else {
                    ans = x2;
                    l = x1 + EPS;
                }
            }

            double maxDist = f(v[i], ans);
            //printf("%.10f, %.10f\n", ans, maxDist);

            double angle;
            if (sgn(maxDist - L1) >= 0) {
                angle = find1(-0.5 * PI + EPS, ans, L1, v[i]);
                ang[cntAng++] = Event(angle, 1, i);
                angle = find2(ans, 0.5 * PI - EPS, L1, v[i]);
                ang[cntAng++] = Event(angle, -1, i);
                if (sgn(maxDist - R1) >= 0) {
                    angle = find1(-0.5 * PI + EPS, ans, R1, v[i]);
                    ang[cntAng++] = Event(angle, -1, i);
                    angle = find2(ans, 0.5 * PI - EPS, R1, v[i]);
                    ang[cntAng++] = Event(angle, 1, i);
                }
                else {
                    ang[cntAng++] = Event(ans, -1, i);
                    ang[cntAng++] = Event(ans, 1, i);
                }
            }
            if (sgn(maxDist - L2) >= 0) {
                angle = find1(-0.5 * PI + EPS, ans, L2, v[i]);
                ang[cntAng++] = Event(angle, -INF, i);
                angle = find2(ans, 0.5 * PI - EPS, L2, v[i]);
                ang[cntAng++] = Event(angle, INF, i);
                if (sgn(maxDist - R2) >= 0) {
                    angle = find1(-0.5 * PI + EPS, ans, R2, v[i]);
                    ang[cntAng++] = Event(angle, INF, i);
                    angle = find2(ans, 0.5 * PI - EPS, R2, v[i]);
                    ang[cntAng++] = Event(angle, -INF, i);
                }
                else {
                    ang[cntAng++] = Event(ans, INF, i);
                    ang[cntAng++] = Event(ans, -INF, i);
                }
            }
        }

        sort(ang, ang + cntAng);

        //for (int i = 0; i < cntAng; i++) {
            //printf("%.10f %d %d\n", ang[i].ang, ang[i].value, ang[i].id);
        //}
        int ans = 0;
        int sum = 0;
        for (int i = 0; i <cntAng; i++) {
            if (ang[i].value == 1) {
                cnt[ang[i].id]++;
                if (cnt[ang[i].id] == 1) {
                    sum += ang[i].value;
                }
            }
            if (ang[i].value == -1) {
                cnt[ang[i].id]--;
                if (cnt[ang[i].id] == 0) {
                    sum += ang[i].value;
                }
            }
            if (ang[i].value == -INF || ang[i].value == INF) {
                sum += ang[i].value;
            }
            if (ans < sum) {
                ans = sum;
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}


你可能感兴趣的:([HDU 4445 Crazy Tank] 三分最远角度+二分命中区间+前缀和求最大值)