版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83146001
传送门
解析:
我看大家这道题都是写的凸包二分找斜率啊。
我就来一发旋转卡壳的题解吧。
思路:
首先,怎么判断一条直线是否穿过这个点集?
显然若它将这个点集分为两份,则它必然把这个点集的凸包分为两份,所以为了简化问题,我们先对点集作一个凸包。
之后怎么判断该直线是否穿过这个凸包?
我们找两条斜率与这条直线相等的线,卡住凸包,然后判断者两条直线是否在原直线的同一侧。如果在原直线同一侧,则凸包整体在原直线一侧,否则直线必然从凸包中间穿过。
那么就显然二分和旋转卡壳都可以做了。
旋转卡壳基本思路就是把所有直线离线下来,按斜率排个序,然后再在凸包上跑旋转卡壳。这样两个指针都只需要 的挪动就可以完美求出所有直线的答案。
当然,在具体实现的时候需要一点转化。比如我们在求两条斜率与原直线相等的直线卡住凸包的时候,先确定一个点,然后我们在这条直线上再确定一个点(任意选取),将另一个指针移动,找到以这三个点面积最大的三角形,就是我们要求的对面的直线。
trick:
注意特判点数为 和点数为 的情况,前者会让凸包在排序的时候 ,后者会让旋转卡壳调整指针的时候陷入死循环。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline double getdb(){
re double x,y=1.0;
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;x=c^48;
while(isdigit(c=gc()))x=(x*10)+(c^48);
if(c!='.')return f?-x:x;
while(isdigit(c=gc()))x+=(y/=10)*(c^48);
return f?-x:x;
}
cs double eps=1e-8;
cs int N=100005;
struct Point{
double x,y;
Point(cs double &_x=0,cs double &_y=0):x(_x),y(_y){}
friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
friend Point operator*(cs Point &a,cs double &b){return Point(a.x*b,a.y*b);}
friend double operator*(cs Point &a,cs Point &b){return a.x*b.y-b.x*a.y;}
friend double dot(cs Point &a,cs Point &b){return a.x*b.x+a.y*b.y;}
double norm()cs{return sqrt(dot(*this,*this));}
}p[N];
int n;
inline int sign(cs double &x){
return fabs(x)<eps?0:(x>0?1:-1);
}
inline bool cmp1(cs Point &a,cs Point &b){
return a.x==b.x?a.y<b.y:a.x<b.x;
}
inline bool cmp2(cs Point &a,cs Point &b){
return sign((a-p[1])*(b-p[1]))==0?(a-p[1]).norm()<(b-p[1]).norm():sign((a-p[1])*(b-p[1]))>0;
}
inline void convex_hull(int m){
sort(p+1,p+m+1,cmp1);
sort(p+2,p+m+1,cmp2);
n=1;
for(int re i=2;i<=m;++i){
while(n>=2&&sign((p[n]-p[n-1])*(p[i]-p[n-1]))<=0)--n;
p[++n]=p[i];
}
p[n+1]=p[1];
p[0]=p[n];
}
struct Line{
Point s,e;
double rad;
int id;
Line(cs Point &a=Point(0,0),cs Point &b=Point(0,0)):s(a),e(b){}
friend bool operator<(cs Line &a,cs Line &b){
return a.rad<b.rad;
}
}line[N];
int m;
inline double trangle(cs Point &a,cs Point &b,cs Point &c){
return fabs((a-b)*(c-b))/2;
}
bool ans[N];
inline int nxt(int i){return i%n+1;}
inline int pre(int i){return i==1?n:i-1;}
inline void solve(int i){
static int l=1,r=1;
while(sign((p[l]-p[pre(l)])*(line[i].e-line[i].s))==sign((p[nxt(l)]-p[l])*(line[i].e-line[i].s)))l=nxt(l);
if(i==1)r=nxt(l);
while(trangle(p[l],p[l]+line[i].e-line[i].s,p[nxt(r)])>trangle(p[l],p[l]+line[i].e-line[i].s,p[r]))r=nxt(r);
ans[line[i].id]=sign((line[i].s-line[i].e)*(p[l]-line[i].e))*sign((line[i].s-line[i].e)*(p[r]-line[i].e))>0;
}
signed main(){
scanf("%d",&n);
for(int re i=1;i<=n;++i){
p[i].x=getdb();
p[i].y=getdb();
}
if(n!=0)
convex_hull(n);
double sx,sy,ex,ey;
while(scanf("%lf",&sx)!=EOF){
sy=getdb(),ex=getdb(),ey=getdb();
line[++m]=Line(Point(sx,sy),Point(ex,ey));
line[m].rad=atan2(line[m].s.y-line[m].e.y,line[m].s.x-line[m].e.x);
line[m].id=m;
}
if(n==0||n==1){
for(int re i=1;i<=m;++i)puts("GOOD");
return 0;
}
sort(line+1,line+m+1);
for(int re i=1;i<=m;++i){
solve(i);
}
for(int re i=1;i<=m;++i){
puts(ans[i]?"GOOD":"BAD");
}
return 0;
}