1. 程式人生 > 實用技巧 >POJ 3335 半平面交模板

POJ 3335 半平面交模板

題意

以順時針或逆時針順序給定一個多邊形,問該多邊形是否有核

解法

半平面交要求邊要按逆時針順序,首先利用叉積判斷給定點順序為逆時針還是順時針,然後按逆時針方向建邊,最後跑半平面交演算法。

程式碼:

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e3+5;
const double eps = 1e-8;
int sgn(double x) {
    if(fabs(x) < eps)
        return 0;
    if(x < 0)
        return -1;
    return 1;
}
struct Point {
    double x,y;
    Point() {}
    Point(double _x,double _y) {
        x = _x;
        y = _y;
    }
    Point operator -(const Point &b)const {
        return Point(x - b.x,y - b.y);
    }
    double operator ^(const Point &b)const {
        return x*b.y - y*b.x;
    }
    double operator *(const Point &b)const {
        return x*b.x + y*b.y;
    }
    Point Rotate(double rad){ //逆時針旋轉
        return Point(x*cos(rad)-y*sin(rad),x*sin(rad)+y*cos(rad));
    }
    double angle(){
        return atan2(y,x);
    }
    double len(){
        return sqrt(x*x+y*y);
    }
};
double xmult(Point p0,Point p1,Point p2) { //p0p1 X p0p2
    return (p1-p0)^(p2-p0);
}
double getArea(Point a,Point b,Point c){
    return fabs(xmult(a,b,c))/2.0;
}
struct Line
{
	Point s,t;
	double ang;
	Line(Point X=Point(),Point Y=Point())
	{
		s=X,t=Y,ang=(Y-X).angle();
	}
	double getangle(){
        return ang=(t-s).angle();
	}
};
Point getIntersectPoint(Line a, Line b) {
  double a1 = a.s.y - a.t.y, b1 = a.t.x - a.s.x, c1 = a.s.x * a.t.y - a.t.x * a.s.y;
  double a2 = b.s.y - b.t.y, b2 = b.t.x - b.s.x, c2 = b.s.x * b.t.y - b.t.x * b.s.y;
  return Point((c1*b2-c2*b1)/(a2*b1-a1*b2), (a2*c1-a1*c2)/(a1*b2-a2*b1));
}
Point p[maxn];//存頂點
Line l[maxn];//存逆時針方向的邊
Line que[maxn];//存半平面交
bool judge(int n){//判斷是否是逆時針的
    double ans=0;
    for(int i=1;i<=n-1;i++){
        ans+=xmult(p[0],p[i],p[i+1]);
    }
    return ans>0;
}
bool onRight(Line a,Line b,Line c){
    Point jiao=getIntersectPoint(b,c);
    if(xmult(a.s,a.t,jiao)<0){
        return 1;
    }
    else{
        return 0;
    }
}
bool cmp(Line a,Line b){
    double A=a.getangle(),B=b.getangle();
    if(sgn(A-B)==0){//平行的直線將最左邊的放後面,便於去重
        return xmult(a.s,a.t,b.t)>=0;
    }
    else{
        return A<B;
    }
}
bool halfPlaneIntersect(int n){
    sort(l+1,l+1+n,cmp);
    int left=1,right=0,cnt=0;
    for(int i=1;i<n;i++){
        if(sgn(l[i].getangle()-l[i+1].getangle())==0){
            continue;
        }
        l[++cnt]=l[i];
    }
    l[++cnt]=l[n];

    for(int i=1;i<=cnt;i++){
        while(right-left+1>=2&&onRight(l[i],que[right],que[right-1])) right--;
        while(right-left+1>=2&&onRight(l[i],que[left],que[left+1])) left++;
        que[++right]=l[i];
    }
    while(right-left+1>=3&&onRight(que[left],que[right],que[right-1])) right--;
    while(right-left+1>=3&&onRight(que[right],que[left],que[left+1])) left++;

    if(right-left+1>=3){
        return 1;
    }
    else{
        return 0;
    }
}
int main () {
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&p[i].x,&p[i].y);
        }
        if(judge(n)){//已經是逆時針
            for(int i=1;i<=n;i++){
                l[i]=Line(p[i],p[i%n+1]);
            }
        }
        else{//是順時針
            for(int i=1;i<=n;i++){
                l[i]=Line(p[i%n+1],p[i]);
            }
        }
        if(halfPlaneIntersect(n)){
            puts("YES");
        }
        else{
            puts("NO");
        }
    }
}