LA 4253 Archery
题目大意:
有n个与x轴平行的线段,每条线段是一个靶子(由D,L,R表示纵坐标为D,左右端点横坐标为L,R),问在x轴的[0,W]区间上是否存在位置可以使得箭穿过所有靶子.假设箭沿直线飞行,直到无穷远处,不同靶子纵坐标不同.
题目分析:
解法一(O(n^2)):
将靶子两两进行枚举,判断i靶子左/右端点与j靶子右/左端点在x轴上的交点形成区间与原区间取交集,若为空,无解.
解法一代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50000+10;
int D[maxn],L[maxn],R[maxn];
double pos(double xi,double yi,double xj,double yj)//计算两点确定的某线段与x轴交点横坐标
{
return xj-yj*(xi-xj)/(yi-yj);
}
void work(int i,int j,double& l,double& r)//计算可行区间
{
if(D[i]<D[j]) swap(i,j);
l=pos(R[i],D[i],L[j],D[j]);
r=pos(L[i],D[i],R[j],D[j]);
}
int W,n;
bool check()//O(n^2)枚举每两条线段,计算可行区间
{
double l=0,r=W,ll,rr;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++) {
work(i,j,ll,rr);
l=max(l,ll);r=min(r,rr);
if(l>r) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&W,&n);
for(int i=0;i<n;i++) scanf("%d%d%d",&D[i],&L[i],&R[i]);
printf("%s\n",check()?"YES":"NO");
}
return 0;
}
解法二(O(nlogn)):
对于某一个点来说,若无法射到某一个靶子,那么就需要进行移动.
那么将该思路引申到二分答案,判断可依据斜率/角度,这里用atan2函数,依据角度判断.
(注:解法二需要先以高度排序,原因在于高和低,底和高的位置移动判断不同)
解法二代码:
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int maxn=50000+10;
struct Point {
int d,l,r;
bool operator < (const Point& rhs) const {
return d<rhs.d;//需要按照高度排序,否则check中无法判断位置应当向那边移动
}
void input() {
scanf("%d%d%d",&d,&l,&r);
}
}P[maxn];
int n;
int check(double pos)//0 可以射穿 1 往右移动 -1 往左移动
{
double l=atan2(P[0].d,P[0].l-pos);//atan2(y,x)得到(y,x)与(0,0)形成的夹角(弧度值)
double r=atan2(P[0].d,P[0].r-pos);
for(int i=1;i<n;i++) {
double ll=atan2(P[i].d,P[i].l-pos);
double rr=atan2(P[i].d,P[i].r-pos);
if(rr-l>eps) return 1;//若角度区间小了,往右移动
if(r-ll>eps) return -1;//若角度区间大了,往左移动
l=min(l,ll);r=max(r,rr);
}
return 0;
}
bool solve(int W)
{
double l=0,r=W;
while(r-l>eps) {
double mid=(l+r)/2;
int k=check(mid);
if(!k) return true;
if(k>0) l=mid;
else r=mid;
}
return false;
}
int main()
{
int T,W;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&W,&n);
for(int i=0;i<n;i++) P[i].input();
sort(P,P+n);
printf("%s\n",solve(W)?"YES":"NO");
}
return 0;
}