【凸包+判斷直線是否與凸包香蕉】 POJ 1912
阿新 • • 發佈:2018-11-10
#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"); } }