凸包浅谈

1 篇文章 0 订阅

今天开始计算几何之旅。

第一章讲的是凸包,之前接触过,想就草草开始下一章,但是想到要与队友共勉,还是认真的看了,来总结一下。

------------------------------------------------

凸包问题大致上就是求解一个点集的最外围点。图就不上了。了解凸包的模型,用模板一般就可以了。自己敲了个模板下文中会贴出来。A了hdu的两题凸包,验证了正确性。


暴力的做法O(n^3)。这里介绍一种O(n*log(n))的方法。

首先对点进行排序。x小的在前面,当x一样大小时,y小的在前面。这样我们就得到了最左边的点和最右边的点。

通过这两个点我们把凸包分成两部分。上半部分和下半部分。

你可以把凸包想象成一个圆,现在我们把圆分成了上下两个半圆。

现在我们有点集中的3个点。且3个点都是有序的如果点2在点1,3所成直线的下方。那么点2必然不属于上凸包。

如此。我们每次都判断从左到右的最后3个点是否符合要求如若不然删除中间的点既点2;这样一直到所有点都遍历了,我们就得到了上凸包;

同理可得下凸包

再介绍一下叉积,高等数学应该学过。他可以判断点在直线的上方还是下方。


如果纯文字看不明白,结合一下代码

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<iostream>
using namespace std;
const int MAX = 1e6+5;
const double pi = 3.1415926;
typedef int element;
struct Point{
    element x , y ;
    friend bool operator < ( Point p1 , Point p2 ){
        return p1.x<p2.x || ( p1.x==p2.x && p1.y > p2.y );
    }

};
double dist( Point p1 , Point p2 ){
    return sqrt( (double)(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) );
}
struct Tubao{
    Point lower[MAX] , upper[MAX];//分别保存上凸包和下凸包的点。
    Point allPoint[MAX];
    int Size;
    int topLower , topUpper;
    void init(){
        sort(allPoint,allPoint+Size);
    }
    //叉积
    element cross( Point p1, Point p2 , Point p3 ){
        return (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x);
    }

    void work(){
        init();
        topUpper = topLower = 0;

        for(int i = 0 ; i < Size ; ++ i ){
            while( cross( upper[topUpper-2] , upper[ topUpper-1 ] , allPoint[i] ) > 0 && topUpper >= 2 )
                topUpper--;//删除不属于凸包上的点
            upper[ topUpper++ ] = allPoint[i] ;
        }

        for(int i = Size-1 ; i >= 0 ; i-- ){
             while( cross( lower[topLower-2] , lower[ topLower-1 ] , allPoint[i] ) > 0 && topLower >= 2 )
                topLower --;
            lower[ topLower++ ] = allPoint[i] ;
        }
        /*for( int i = 1 ; i < topUpper-1 ; ++ i )
            lower[ topLower ++ ] = upper[ i ] ;*/
    }
}Solve;
//以下非模板
int main(){

int t;scanf("%d",&t);
while(t--){
    int m,k;
    int n;scanf("%d%d%d",&k,&m,&n);
        Solve.Size = n;
        for(int i = 0 ; i < n ;  ++ i )
            scanf("%d%d" , &Solve.allPoint[i].x , &Solve.allPoint[i].y );
        if(n==1){
            puts("0.00");continue;
        }
        if(n==2){
            printf("%.2lf\n" , dist(Solve.allPoint[0],Solve.allPoint[1]));continue;
        }
        Solve.work();
        double ret = 0;
        for(int i = 0 ; i < Solve.topLower ; ++ i ){
            ret += max(0,-Solve.lower[i].x*k+Solve.lower[i].y*m);
        }
        for(int i = 1 ; i < Solve.topUpper-1 ; ++ i ){
            ret += max(0,-Solve.upper[i].x*k+Solve.upper[i].y*m);
        }
        printf("%.0lf\n" , ret);


}
    return 0;
}
/*
12 7

30 5

80 7
50 87
22 9
45 1
50 7
*/


与队友共勉:如果还是不理解凸包,我把电子书传到讨论组,可以看第一章。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值