凸包几种构造方法+旋转卡壳

一、分治法

找到横坐标最小和最大的两个点p0p1,它们一定在凸包上(横坐标最小、最大的若多个点,随意取一个即可),然后在直线p0p1的其中一边找离直线最远的一个点,它也一定在凸包上,以后每次向外部(内部的点一定不是凸包上的点)找离现有直线最远的一个点;再在直线p0p1的另一边进行同样的操作

实现细节:一开始将点用xy坐标进行双关键字排序,事实上每次找到一个点之后,相当于把原(矩形)空间分成了两个子(矩形)空间,距离直接用三角形有向面积判断即可,有向面积判断还能顺便判断是在内部还是外部

时间复杂度事实上和快排一样取决于数据分布,一般认为是O(nlogn)

 

二、Jarvis步进法

找到纵坐标最小的点作为起点,每次找极角最小(相等则取最远)的点作为凸包下一个点

时间复杂度O(hn)h为凸包上点数

 

三、Graham扫描法

找到纵坐标最小的点作为起点,以该点为极点进行极角排序,用一个栈维护前进方向

时间复杂度O(nlogn)

 

四、Andrew算法(推荐使用)

xy进行双关键字排序,找到横坐标最小的点,然后用一个栈维护前进方向,求一个下凸包再求一个上凸包

时间复杂度O(nlogn)

 hdu1392 一道裸凸包题,但数据有点奇怪,n=2时要特判

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define rep(i,a,b) for(int i=a;i<=b;++i)
#define arcrep(i,a,b) for(int i=a;i>=b;--i)

const int MAXN=200000+10;

struct point{
    long long x,y;
    point(){}
    point (long long a,long long b): x(a),y(b) {}

    friend point operator + (const point &a,const point &b){
        return point(a.x+b.x,a.y+b.y);
    }

    friend point operator - (const point &a,const point &b){
        return point(a.x-b.x,a.y-b.y);
    }

    friend point operator * (const long long &r,const point &a){
        return point(r*a.x,r*a.y);
    }

    friend bool operator == (const point &a,const point &b){
        return a.x==b.x && a.y==b.y;
    }

    double norm(){
        return sqrt(x*x+y*y);
    }
};

inline long long det(point a,point b) {return a.x*b.y-a.y*b.x;}
inline long long dot(point a,point b) {return a.x*b.x+a.y*b.y;}
inline double dist(point a,point b) {return (a-b).norm();}


point ori[MAXN+10],con[MAXN+10];

bool cmp(const point &a,const point &b)
{
    return (a.x<b.x || (a.x==b.x && a.y<b.y));
}

//Graham算法 input:ori数组 output:con数组
int Graham(int n)
{
    sort(ori+1,ori+n+1,cmp);        //排序后第1和第n个点必定属于凸包
    int temp=0;
    rep(i,1,n)
    {
        while (temp>=2 && det(con[temp]-con[temp-1],ori[i]-con[temp])<=0) --temp;
        con[++temp]=ori[i];
    }
    int ans=temp;
    arcrep(i,n-1,1)
    {
        while (ans>temp && det(con[ans]-con[ans-1],ori[i]-con[ans])<=0) --ans;
        con[++ans]=ori[i];
    }
    if (n>1) --ans;
    return ans;
}

int n,num;
double ans;
int main() { while (~scanf("%d",&n)) { if (n==0) break; rep(i,1,n) scanf("%lld%lld",&ori[i].x,&ori[i].y); if (n==2) {printf("%.2f\n",dist(ori[1],ori[2]));continue;} num=Graham(n); ans=0; rep(i,1,num-1) ans+=dist(con[i],con[i+1]); ans+=dist(con[num],con[1]); printf("%.2f\n",ans); } return 0; }

 

旋转卡壳的核心思路是寻找对踵点

旋转卡壳裸题poj2187(可直接用int或者long long)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define rep(i,a,b) for(int i=a;i<=b;++i)
#define arcrep(i,a,b) for(int i=a;i>=b;--i)

const int MAXN=200000+10;
struct point{
    int x,y;
    point(){}
    point (int a,int b): x(a),y(b) {}

    friend point operator + (const point &a,const point &b){
        return point(a.x+b.x,a.y+b.y);
    }

    friend point operator - (const point &a,const point &b){
        return point(a.x-b.x,a.y-b.y);
    }

    friend point operator * (const int &r,const point &a){
        return point(r*a.x,r*a.y);
    }

    friend bool operator == (const point &a,const point &b){
        return a.x==b.x && a.y==b.y;
    }

    int sqrnorm(){
        return x*x+y*y;
    }
};

inline int det(point a,point b) {return a.x*b.y-a.y*b.x;}
inline int dot(point a,point b) {return a.x*b.x+a.y*b.y;}
inline int sqrdist(point a,point b) {return (a-b).sqrnorm();}

point ori[MAXN+10],con[MAXN+10];

bool cmp(const point &a,const point &b)
{
    return (a.x<b.x || (a.x==b.x && a.y<b.y));
}

//Graham算法 input:ori数组 output:con数组
int Graham(int n)
{
    sort(ori+1,ori+n+1,cmp);        //排序后第1和第n个点必定属于凸包
    int temp=0;
    rep(i,1,n)
    {
        while (temp>=2 && det(con[temp]-con[temp-1],ori[i]-con[temp])<=0) --temp;
        con[++temp]=ori[i];
    }
    int ans=temp;
    arcrep(i,n-1,1)
    {
        while (ans>temp && det(con[ans]-con[ans-1],ori[i]-con[ans])<=0) --ans;
        con[++ans]=ori[i];
    }
    if (n>1) --ans;
    return ans;
}

int mindist(int m)
{
    int ans=0;
    int temp;
    con[m+1]=con[1];
    int j=2;
    for (int i=1;i<=m;++i)
    {

        while (abs(det(con[j]-con[i],con[i+1]-con[i]))<abs(det(con[j+1]-con[i],con[i+1]-con[i])))
        {
            ++j;
            if (j>m) j=1;
        }
        temp=sqrdist(con[i],con[j]);
        if (temp>ans)
            ans=temp;
    }
    return ans;
}

int n,m;
int ans;

int main()
{
    scanf("%d",&n);
    rep(i,1,n) scanf("%d%d",&ori[i].x,&ori[i].y);
    m=Graham(n);
    ans=mindist(m);
    printf("%d\n",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/terra/p/7245216.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值