UVALive 4253 Archery(二分+atan2应用)

来源:互联网 发布:淘宝发布宝贝怎么预览 编辑:程序博客网 时间:2024/06/06 07:42

题意:

有 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;}
0 0
原创粉丝点击