求解凸包的两种算法

本文探讨了Graham算法和Andrew算法在计算凸包中的应用,两者的复杂度均为O(n log n),但Andrew算法在效率上更优。重点讲解了极角排序和向量叉积在判断点位置和共线性的关键作用,以及如何避免在访问凸包时的问题。适合理解这两种常见几何算法的读者。
摘要由CSDN通过智能技术生成

Graham 算法

O ( n ∗ l o g n ) O(n*logn) O(nlogn)
求得凸包点按逆时针转

Template

int dcmp(double x, double y){
    if(fabs(x - y) < eps)
        return 0;
    if(x > y)
        return 1;
    return -1;
}
struct Point{
    double x,y;
    Point(double x = 0,double y = 0):x(x),y(y){ } // 构造函数
    bool operator < (const Point&B)const{return y < B.y || y == B.y && x < B.x;}
};
double Length(Vector A) {return sqrt(Dot(A,A));}
// 点积   夹角与点积的关系
double Dot(Vector A,Vector B){return A.x*B.x+A.y*B.y;}
// 叉积
double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}
// 极角排序  叉乘实现


bool cmp(Point a,Point b){
    double t = Cross(a-p[1],b-p[1]);
    if(sgn(t) > 0) return true;
    if(sgn(t) == 0 && dcmp(Length(a-p[1]),Length(b-p[1])) < 0) return true;
    return false;
 }

void Graham(){
    sort(p+1,p+1+n);// 重载小于< 运算符找到最左下角的点
    sort(p+2,p+1+n,cmp); 
    stk[++top] = 1,stk[++top] = 2;
    forr(i,3,n){
        while(top >=2 && sgn(Cross(p[stk[top]]-p[stk[top-1]],p[i] - p[stk[top-1]])) <= 0) top--; // < 共线加入凸包
        stk[++top] = i;
    }
}

Andrew 算法

O ( n ∗ l o g n ) O(n*logn) O(nlogn)
不需要极角排序较优
注意 访问凸包要通过 s t k stk stk 吃过一次亏


double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}
// 点 c 是否在向量ab的左边
// >=  共线不加入凸包
bool ToLeftTest(Point a,Point b,Point c){return Cross(b-a,c-a) > 0;}


int stk[1005];
bool used[1005];
void Andrew(){
    sort(p+1,p+1+n);
    int top = 0;
    forr(i,1,n){
        while(top >= 2 && ToLeftTest(p[stk[top-1]],p[stk[top]],p[i])) used[stk[top--]] = 0;
        stk[++top] = i;
        used[i] = 1;
    }
    used[1] = 0;
    for(int i = n;i;i--){
        if(used[i]) continue;
        while(top >= 2 && ToLeftTest(p[stk[top-1]],p[stk[top]],p[i])) top--;
        stk[++top] = i;     
    }
}

例题

POJ 3348

int n;
Point p[100005];
int stk[100005],top;
struct Point{
    double x,y;
    Point(double x = 0,double y = 0):x(x),y(y){ } // 构造函数
    bool operator < (const Point&B)const{return y < B.y || y == B.y && x < B.x;}

};

bool cmp(Point a,Point b){
    double t = Cross(a-p[1],b-p[1]);
    if(sgn(t) > 0) return true;
    if(sgn(t) == 0 && dcmp(Length(a-p[1]),Length(b-p[1])) < 0) return true;
    return false;
 }
void Graham(){
    sort(p+2,p+1+n,cmp);
    
    stk[++top] = 1,stk[++top] = 2;
    forr(i,3,n){
        while(top >=2 && sgn(Cross(p[stk[top]]-p[stk[top-1]],p[i] - p[stk[top-1]])) <= 0) top--;
        stk[++top] = i;
    }
}
signed main()
{
    cin>>n;
    forr(i,1,n)cin >> p[i].x >> p[i].y;
    sort(p+1,p+1+n);
    Graham();
    double are = 0;
    forr(i,2,top-1){
        are +=  Cross(p[stk[i]]-p[1],p[stk[i+1]]-p[1])/2.0;
    }
    are = fabs(are/50.0);
    printf("%.0f\n",floor(are));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值