【凸包+判断直线是否与凸包相交】 POJ 1912

你~需要我的帮助吗

【题目大意】给出平面上的 n 个点,对于 m 条直线,依次判断这 n 个点是否在每条直线的同一侧。(n,m≤1e5)

【思路】首先对于n个点,求出凸包。然后对于一个直线l,判断它是否与凸包香蕉:作两条与l平行的直线,把凸包卡住。看两个切点的连线是否与直线有交点。没有交点就符合要求,有交点就不行。我们只需要判一下这两个切点是否在这条直线的同一侧就行了。大概就是取直线上的一个顶点A,把A到两个切点的向量搞出来,再叉积一下,看结果是否大于0,若大于0,在同侧,否则在异侧。

【自己写的时候发现的错误】:

puts后面不要加\n。。。。。。

中间输出变量忘注释了,一直Output Limit Exceeded。。。

n=1的时候凸包居然炸了【不知道为什么。。。】

特判一下,n<=1时跳过求凸包过程输出GOOD就行了【应该不会有点在直线上吧?】
 

左图为香蕉的情况。

 

左图为相离的情况。

 

由于凸包上的线段,它们的斜率是单调的,所以可以二分。

只要找到第一个与正向直线夹角大于0的线段和第一个与反向直线夹角大于0的线段。

atan2返回的是弧度【注意一下参数前面是y后面是x】。

保证角度的单调性,凸包第一个点设成y最大的点。

可以用  ×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];
}

void solve(){
	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';
	}
}
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;

	if(n>1) solve();
	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");
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值