【凸包+判断直线是否与凸包香蕉】 POJ 1912
#define 香蕉 相交
【题目大意】给出平面上的 n 个点,对于 m 条直线,依次判断这 n 个点是否在每条直线的同一侧。(n,m≤1e5)
【思路】首先对于n个点,求出凸包。然后对于一个直线l,判断它是否与凸包香蕉:作两条与l平行的直线,把凸包卡住。看两个切点的连线是否与直线有交点。没有交点就符合要求,有交点就不行。我们只需要判一下这两个切点是否在这条直线的同一侧就行了。大概就是取直线上的一个顶点A,把A到两个切点的向量搞出来,再叉积一下,看结果是否大于0,若大于0,在同侧,否则在异侧。
【自己写的时候发现的错误】:puts后面不要加\n。。。。。。【吐血】
中间输出变量忘注释了,一直Output Limit Exceeded。。。
左图为香蕉的情况。
左图为相离的情况。
由于凸包上的线段,它们的斜率是单调的,所以可以二分。
只要找到第一个与正向直线夹角大于0的线段和第一个与反向直线夹角大于0的线段。
atan2返回的是弧度【注意一下参数前面是y后面是x】。
可以用 ×180/pi 转成角度,然后输出一下中间的参数来辅助理解或检查程序。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=5e5;
const double eps=1e-8;
const double pi=3.14159265358979323846;
struct point{
double x,y;
point(double _x=0,double _y=0){
x=_x;
y=_y;
}
friend inline point operator +(const point &a,const point &b){
return point(a.x+b.x,a.y+b.y);
}
friend inline point operator -(const point &a,const point &b){
return point(a.x-b.x,a.y-b.y);
}
friend inline double operator *(const point &a,const point &b){
return a.x*b.y-a.y*b.x;
}
}st[maxn],p[maxn],A,B;
int top=0,n,id=1;
double ang[maxn];
double dist(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(const point &a,const point &b){
double K=(a-p[1])*(b-p[1]);
return (K==0)?(dist(a,p[1])<=dist(b,p[1])):K>0; //极角按逆时针排序
}
//求凸包的板子。
void Graham(){
sort(p+2,p+n+1,cmp);
st[++top]=p[1];
for(int i=2;i<=n;++i){
while(top>=3&&((p[i]-st[top-1])*(st[top]-st[top-1])>=0))
top--;
st[++top]=p[i];
}
st[top+1]=st[1];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lf%lf",&p[i].x,&p[i].y);
for(int i=2;i<=n;++i)
if((p[i].y>p[id].y)||((p[i].y==p[id].y)&&(p[i].x<p[id].x)))
id=i;
swap(p[id],p[1]);
Graham();
for(int i=1;i<=top;++i){
// cout<<p[i].x<<' '<<p[i].y<<' ';
ang[i]=atan2(st[i+1].y-st[i].y,st[i+1].x-st[i].x);//*180/pi;
// cout<<ang[i]<<'\n';
}
while(scanf("%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y)!=EOF){
if(n==1){
printf("GOOD\n");
continue;
}
double ANG1=atan2(A.y-B.y,A.x-B.x);//*180/pi;
double ANG2=atan2(B.y-A.y,B.x-A.x);//*180/pi;
//直线取两个方向的。取两次角度分别对应卡住凸包的两条平行线的角度。
// cout<<ANG1<<"______"<<ANG2<<'\n';
int pos1=lower_bound(ang+1,ang+top+1,ANG1)-ang;
int pos2=lower_bound(ang+1,ang+top+1,ANG2)-ang;
// cout<<pos1<<' '<<pos2<<'\n';
int sgn1=((st[pos1]-B)*(A-B)<0)?-1:1;
int sgn2=((st[pos2]-B)*(A-B)<0)?-1:1;
if((sgn1*sgn2)==1) printf("GOOD\n");
else printf("BAD\n");
}
}