2018.10.18【POJ1912】A highway and the seven dwarfs(凸包)(旋转卡壳)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83146001

传送门


解析:

我看大家这道题都是写的凸包二分找斜率啊。
我就来一发旋转卡壳的题解吧。

思路:

首先,怎么判断一条直线是否穿过这个点集?
显然若它将这个点集分为两份,则它必然把这个点集的凸包分为两份,所以为了简化问题,我们先对点集作一个凸包。

之后怎么判断该直线是否穿过这个凸包?

我们找两条斜率与这条直线相等的线,卡住凸包,然后判断者两条直线是否在原直线的同一侧。如果在原直线同一侧,则凸包整体在原直线一侧,否则直线必然从凸包中间穿过。

那么就显然二分和旋转卡壳都可以做了。

旋转卡壳基本思路就是把所有直线离线下来,按斜率排个序,然后再在凸包上跑旋转卡壳。这样两个指针都只需要 O ( n ) O(n) 的挪动就可以完美求出所有直线的答案。

当然,在具体实现的时候需要一点转化。比如我们在求两条斜率与原直线相等的直线卡住凸包的时候,先确定一个点,然后我们在这条直线上再确定一个点(任意选取),将另一个指针移动,找到以这三个点面积最大的三角形,就是我们要求的对面的直线。

trick:

注意特判点数为 0 0 和点数为 1 1 的情况,前者会让凸包在排序的时候 R E RE ,后者会让旋转卡壳调整指针的时候陷入死循环。


代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/83146001