计算几何_凸包

极角排序求凸包      水平排序求凸包

 

极角排序

struct Point {
    double x, y;
    Point () {}
    Point (double _x, double _y) : x(_x), y(_y) {}
    
    Point operator + (const Point &a) const { return Point(x + a.x, y + a.y); }
    
    // 叉积=0是指两向量平行(重合)
    Point operator - (const Point &a) const { return Point(x - a.x, y - a.y); }
    
    double operator ^ (const Point &a) const { return x * a.y - y * a.x; }  // 叉积 
    double operator * (const Point &a) const { return x * a.x + y * a.y; }  // 点积 
    
    // 定义给map set 之类用的.. 不要用极角 可能会出错 
    bool operator <  (const Point &a) const {
        if (x != a.x) return x < a.x;
        return y < a.y;
    }
};

double GetCross(Point st, Point ed1, Point ed2) { return (ed1 - st) ^ (ed2 - st); }

//  叉积极角排序 
//  直接把原点当作 极点  进行 极角排序. 
bool cmp_j_j(const Point &A, const Point &B) {  
    if ((A ^ B) != 0.0) return (A ^ B) > 0.0;
    if (A.x != B.x)  return A.x < B.x;
    return A.y < B.y;
}

//  叉积极角排序. 
//  以Qcmppt为起点 做极角排序. 
Point Qcmppt; 
bool cmp_j_j_j(const Point &A, const Point &B) {  
    int m = (A-Qcmppt) ^ (B-Qcmppt);
    if (m != 0.0) return m > 0.0;
    if (A.x != B.x)  return A.x < B.x;
    return false;
}

//  atan2 极角排序
//  atan2 赛高  听说速度快些 但是精度比叉积差些  听说,.,, 未实践 
bool cmp_o_o(const Point &A, const Point &B) { 
    if (atan2(A.y, A.x) != atan2(B.y, B.x)) return atan2(A.y, A.x) < atan2(B.y, B.x);
    if (A.x != B.x)  return A.x < B.x;
    return A.y < B.y;
}

// ---------------------------------------------------------
// 凸包 极角排序   不能用于共线点
// 要把第一个点(最左下角的点)放在最前面 而且sort的时候不包含进去.
// 该点是起点 
for (i=1; i<=n; ++i) {
    scanf("%lf%lf", &pt[i].x, &pt[i].y);
    pt[i].flag = false;
    if (pt[i].y < pt[1].y)  swap(pt[i], pt[1]);
    else if (pt[i].y == pt[1].y && pt[i].x < pt[1].x) swap(pt[i], pt[1]);
} 
Qcmppt = pt[1]; // Qcmppt 全局变量 
sort(pt+2, pt+1+m+n, cmp_j_j_j);   // 排序方法也可以用 atan2 排序 cmp_o_o() 
head = 0;
for (i=1; i<=n; ++i) {
    while  (head>1 && GetCross(pt[st[head-1]], pt[st[head]], pt[i]) <= 0.0) head--;
    st[++head] = i;
}
// head 就是点的个数 

 

水平排序求凸包

struct Point {
    double x, y;
    Point () {}
    Point (double _x, double _y) : x(_x), y(_y) {}
    
    Point operator + (const Point &a) const { return Point(x + a.x, y + a.y); }
    
    // 叉积=0是指两向量平行(重合)
    Point operator - (const Point &a) const { return Point(x - a.x, y - a.y); }
    
    double operator ^ (const Point &a) const { return x * a.y - y * a.x; }  // 叉积 
    double operator * (const Point &a) const { return x * a.x + y * a.y; }  // 点积 
    
    // 定义给map set 之类用的.. 不要用极角 可能会出错 
    bool operator <  (const Point &a) const {
        if (x != a.x) return x < a.x;
        return y < a.y;
    }
};

double GetCross(Point st, Point ed1, Point ed2) { return (ed1 - st) ^ (ed2 - st); }

// 凸包  水平排序 允许有重点
// 点从1到n 
sort(pt+1, pt+n+1, cmp_s_p);
head = 0;
for (i=1; i<=n; ++i) {
    // 如果 不希望 有"三点共线", 重点 把 下面的 两个 < 都改成 <= 
    while  (head>1 && GetCross(pt[st[head-1]], pt[st[head]], pt[i]) < 0.0) head--;
    st[++head] = i;
}
int t = head;
for (i=n-1; i>=1; --i) { // 注意是n-1 
    while  (head>=t && GetCross(pt[st[head-1]], pt[st[head]], pt[i]) < 0.0) head--;  // 貌似head>=t  不然wa....原理嘛...不是很懂.
    st[++head] = i;
} 
// 答案会把起点包含2次 所以看情况 head减1 

 

posted @ 2018-08-03 08:18  过路人1998  阅读(209)  评论(0编辑  收藏  举报