1. 程式人生 > >【凸包+判斷直線是否與凸包香蕉】 POJ 1912

【凸包+判斷直線是否與凸包香蕉】 POJ 1912

你~需要我的幫助嗎

#define 香蕉 相交

【題目大意】給出平面上的 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");
	}
}