点、线段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
const double eps=1e-8;
inline int sgn(double x){
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}

struct Point{
    double x,y;
    Point(){
        x=0;
        y=0;
    }
    Point(double a,double b):x(a),y(b){}
    inline Point operator-(const Point& b)const{
        return Point(x-b.x,y-b.y);
    }
    inline Point operator+(const Point& b)const{
        return Point(x+b.x,y+b.y);
    }
    inline double dot(const Point& b)const{
        return x*b.x+y*b.y;
    }
    inline double cross(const Point&b,const Point& c)const{
        return (b.x-x)*(c.y-y)-(c.x-x)*(b.y-y);
    }
    double operator^(Point a){return x*a.y-y*a.x;}
    double operator*(Point a){return x*a.x+y*a.y;}
};

double dist(const Point& a, const Point& b){
    return sqrt((b-a).dot(b-a));
}



//得线段交点
Point LineCross(const Point &a, const Point &b, const Point &c, const Point &d) {
     double u = a.cross(b, c), v = b.cross(a, d);
     return Point((c.x * v + d.x * u) / (u + v), (c.y * v + d.y * u) / (u + v));
}

//判断线段和直线相交 poj3304
//s-e是直线
bool check(const Point& s,const Point& e){
    if(sgn(dist(s,e))==0) return false;	//注意特殊情况,线段退化为点时会造成共线的误判
    for(int i=0;i<n;i++){
        if(sgn(s.cross(lines[i][0],e))*sgn(s.cross(lines[i][1],e))>0)
           return false;
    }
    return true;
}

凸包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Point stk[maxn];
bool operator<(const Point& a,const Point& b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
int Andrew(){	//逆时针扫描
    sort(ps,ps+n);
    int top=0;
    for(int i=0;i<n;i++){
        while(top>1&&sgn((stk[top-1]-stk[top-2])^(ps[i]-stk[top-1]))<=0) top--;
        stk[top++]=ps[i];
    }
    int k=top;
    for(int i=n-2;i>=0;i--){
        while(top>k&&sgn((stk[top-1]-stk[top-2])^(ps[i]-stk[top-1]))<=0) top--;
        stk[top++]=ps[i];
    }
    return top;	//凸包点数
}//结果点集逆时针存放在stk中

半平面交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//poj3335求多边形有无核

struct Line {
    Point s, e;
    Line() {}
    Line(Point a, Point b) {s = a, e = b;}
};
double getAngle(Line a){
    return atan2(a.e.y-a.s.y,a.e.x-a.s.x);
}
bool operator<(Line a,Line b){
    double A=getAngle(a);
    double B=getAngle(b);
    if(fabs(A-B)<eps) return ((a.e-a.s)^(b.e-a.s))>0;  //把靠右的排在前面
    else return A<B;
}
Point getIntersectPoint(Line a,Line b){     //用线段b的两点坐标组合成交点坐标
    double u=a.s.cross(a.e,b.e);
    double v=a.e.cross(a.s,b.s);
    return Point((v*b.e.x+u*b.s.x)/(u+v),(v*b.e.y+u*b.s.y)/(u+v));
}


bool onRight(Line a,Line b,Line c){ //判断bc交点是否在a的右边
    Point o=getIntersectPoint(b,c);
    if(((a.e-a.s)^(o-a.s))<0)   //这里不能取等,也许是因为能留下一条边就尽量留?这样tail-head的合法判定更易满足
        return true;
    return false;
}

Line lines[maxn];
Line que[maxn];
Point ps[maxn];
int n;

bool HalfPlaneIntersect(){
    sort(lines,lines+n);
    int head=0,tail=0,cnt=0;
    for(int i=0;i<n-1;i++){
        if(fabs(getAngle(lines[i])-getAngle(lines[i+1]))<eps){
            continue;
        }
        lines[cnt++]=lines[i];
    }
    lines[cnt++]=lines[n-1];

    for(int i=0;i<cnt;i++){
        while(tail-head>1&&onRight(lines[i],que[tail-1],que[tail-2]))   //用新边优化队尾
            tail--;
        while(tail-head>1&&onRight(lines[i],que[head],que[head+1])) //用新边优化队首
            head++;
        que[tail++]=lines[i];
    }
    while(tail-head>1&&onRight(que[head],que[tail-1],que[tail-2]))
        tail--;
    while(tail-head>1&&onRight(que[tail-1],que[head],que[head+1]))
        head++;
    if(tail-head<3)
        return false;
    return true;
}

bool judge() {  //判断点的顺序是逆时针
  double ans = 0;
  for(int i=1; i<n-1; i++) {
    ans += ((ps[i]-ps[0])^(ps[i+1]-ps[0]));
  }
  return ans>0;
}
int t;
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=0;i<n;i++)
            cin>>ps[i].x>>ps[i].y;
        if(judge()){
            for(int i=0;i<n;i++)
                lines[i]=Line(ps[i],ps[(i+1)%n]);
        }else{
            for(int i=0;i<n;i++){
                lines[i]=Line(ps[(i+1)%n],ps[i]);
            }
        }
        if(HalfPlaneIntersect()){
            printf("YES\n");
        }else
            printf("NO\n");
    }
    return 0;
}

旋转卡壳

求多边形最长直径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//注意它的思想,实际情况中可能求的不是直径
double rotating_caliper(Point* ps){
    double res=0;
    if(n==2){
        res=dist(ps[0],ps[1]);
    }else{
        int j=2;
        ps[n]=ps[0];	//省的再对i取模……
        for(int i=0;i<n;i++){
            while(ps[i].cross(ps[i+1],ps[j])<ps[i].cross(ps[i+1],ps[j+1]))
                j=(j+1)%n;
            res=max(res,max(dist(ps[i],ps[j]),dist(ps[i+1],ps[j])));
        }
    }
    return res;
}