UVALive 4253 Archery(二分+atan2应用)

本文介绍了一种解决如何在指定范围内选择最优站位,以便使用箭矢穿透一系列平行于x轴的靶子的问题。通过二分搜索确定最佳位置,并利用反正切函数计算所需角度,实现算法的高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

有 n 个平行于 x 轴的线段,每条线段代表一个靶子。你的任务是判断是否可以站在 x 轴上[0,W]区间内的摸个位置舌尖,使得箭能穿过所有的靶子。假设沿着直线飞信,直到无穷远处。

解析:

二分人站的位置,然后去判断,判断过程中计算每个靶子需要的角度[L,R],维护[L,R]区间,先进行按y排序,这样一来,如果一个靶子在LR左边,那么人肯定向左移动,反之人要向右移动。如果找到一个位置合适就return true。

注意:

在C语言的math.h或C++中的cmath中有两个求反正切的函数atan(double x)与atan2(double y,double x) 他们返回的值是弧度 要转化为角度再自己处理下。

前者接受的是一个正切值(直线的斜率)得到夹角,但是由于正切的规律性本可以有两个角度的但它却只返回一个,因为atan的值域是从-90~90 也就是它只处理一四象限,所以一般不用它。

第二个atan2(double y,double x) 其中y代表已知点的Y坐标 同理x ,返回值是此点与远点连线与x轴正方向的夹角,这样它就可以处理四个象限的任意情况了,它的值域相应的也就是-180~180了

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const double eps = 1e-7;
const int N = 5005;
const int MAXW = 1e7 + 5;
struct Node {
    double d, l, r;
}node[N];

int n, w;

bool cmp(Node a, Node b) {
    return a.d < b.d;
}

/**
 * 0代表正好穿过
 * -1代表偏左
 *  1代表偏右
 */

int judge(double x0) {
    double y0 = node[0].d;

    double lo = atan2(y0, node[0].r - x0);
    double ro = atan2(y0, node[0].l - x0);

    double x, y, x1, x2;

    for(int i = 1; i < n; i++) {
        y = node[i].d;

        x = (node[i].r - x0);
        double lo2 = atan2(y, x);

        x = (node[i].l - x0);
        double ro2 = atan2(y, x);

        if(ro2 - lo < -eps) return -1;
        if(lo2 - ro > eps) return 1;

        lo = max(lo, lo2), ro = min(ro, ro2);
    }
    return 0;
}

bool check() {
    double L = 0, R = w * 1.0;
    while(R - L > eps) {
        double M = (L + R)/2;
        int flag = judge(M);
        if(flag == 0) return true;
        else if(flag < 0)
            R = M;
        else
            L = M;
    }
    return false;
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &w, &n);
        for(int i = 0; i < n; i++) {
            scanf("%lf%lf%lf", &node[i].d, &node[i].l, &node[i].r);
        }
        sort(node, node+n, cmp);
        printf("%s\n", check() ? "YES" : "NO");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值