题意:
有 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;
}