几何模版-凸包

Graham算法

平均复杂度:N log(N)

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

const int Max = 1100;
#define PI 3.1415926
struct Point
{
    int x;
    int y;
};
int num;
Point p[Max];   //原始点
Point ch[Max];  //凸包点
//叉乘
int xmult(const Point& p0, const Point& p1, const Point& p2)
{
    return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y);
}
//间距
double dis(const Point& a, const Point& b)
{
    return sqrt((double)(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
//极角排序
bool cmp(const Point& a, const Point& b)
{
    int ret = xmult(p[0],a,b);
    if(ret>0)
        return true;
    if(ret==0&&dis(p[0],a)<dis(p[0],b))
        return true;
    return false;
}


//Graham(p,n,ch,num);
//求n个点构成的p点集中的凸包
//凸包上共num个点按序存于ch数组里。
void Graham(Point* p, int n, Point* ch, int& top)
{
    int i,k=0;
    for(i=1; i < n; i++)
    {
        if(p[k].y>p[i].y || ((p[k].y==p[i].y)&&p[k].x>p[i].x))
            k = i;
    }
    swap(p[k],p[0]);//寻找最左下角的点
    sort(p+1,p+n,cmp);
    top = 0;
    ch[top++] = p[0];
    ch[top++] = p[1];
    ch[top++] = p[2];
    for(i = 3; i < n; ch[top++]=p[i++])
        while(top>=2 && xmult(ch[top-2],ch[top-1],p[i])<0)    //左拐判断
            top--;
}

//test:
int main()
{
    int i;
    int n,r,t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&r);
        {
            for(i = 0; i < n; i++)
                scanf("%d%d",&p[i].x,&p[i].y);
            Graham(p,n,ch,num);
            double result = PI*r*2;
            for(i = 0; i < num-1; i++)//周长
                result += dis(ch[i],ch[i+1]);
            result += dis(ch[0],ch[num-1]);
            printf("%.0f\n",result);
        }
        if(t!=0)
            printf("\n");
    }
    return 0;
}

快速凸包算法

复杂度:平均Nlog(2N),最优N,最差N^2

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;
struct point
{
    double x,y;
};
struct point p[105],p1[105];

double s[105];
int d;
bool cmp(point a,point b)
{
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}

double chaji(point a,point b,point c)
{
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}


//求点集p的凸包,凸包上点的个数为d。按序存入p1数组内
void hull(int l,int r,point a,point b)
{         //左边   右边
    int x=l,i=l-1,j=r+1,k;
    point y;
    for(k=l;k<=r;k++)
    if(s[x]<s[k] || s[x]==s[k] && cmp(p[x],p[k])) x=k;
    
    y=p[x];
    for(k=l;k<=r;k++) //左边
    {
        s[++i]=chaji(p[k],a,y);
        if(s[i]>0) swap(p[i],p[k]);
        else i--;
    }
    for(k=r;k>=l;k--)  // 右边
    {
        s[--j]=chaji(p[k],y,b);
        if(s[j]>0) swap(p[j],p[k]);
        else j++;
    }
    if(l<=i) hull(l,i,a,y);
    d++;
    p1[d]=y;
    if(j<=r) hull(j,r,y,b);
}

double dist(point a,point b)
{
    return sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));
}

int main()
{
    int n,i;
    double sum;
    while(~scanf("%d",&n) && n)
    {
        memset(p,0,sizeof(p));
        memset(p1,0,sizeof(p1));
        memset(s,0,sizeof(s));
        sum=0;
        d=1;

        for (i=1;i<=n;i++)
            scanf("%lf %lf",&p[i].x,&p[i].y);
        sort(p+1,p+n+1,cmp); //按x从小到大排,x相同:按y从小到大排
        p1[1].x=p[1].x;      //第一个点 一定为凸包点
        p1[1].y=p[1].y;

        hull(2,n,p[1],p[1]);

        p1[d+1].x=p[1].x;
        p1[d+1].y=p[1].y;
        for(i=1;i<=d;i++) sum+=dist(p1[i],p1[i+1]);
        if(d==2) sum/=2;
        printf("%.2lf\n",sum);
    }
    return 0;
}

 

步进发

复杂度:平均 NM(M为凸包上的点的个数)

适合凸包上的点个数少的时候。即点分布较为随机。

#include <stdio.h>
#include <math.h>
#include <algorithm>
using namespace std;
struct point
{
    double x,y;
};
struct point a[1005],b[1005],p[2005],p1[2005];
//p:遍历全部点 p1:记录凸包的点
int size;         //凸包点个数
bool cmp(point a,point b)
{//按x从小到大排列;x相同的 按y从小到大排列
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;
}
double chaji(point a,point b,point c)
{
    return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double dist(point a,point b)
{
    return sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));
}


//求点集p的凸包。
//凸包上点数为size,按序存回p数组;
void sovle(int n)
{
    int i;
    double t;
    p1[0]=p[0];     //第一个凸包点是第一个点
    p1[1]=p[1]; //假设..二............二....
    size=1;    //记录当前假设的凸包点
    for(i=2;i<n;i++)//遍历所有点
    {
        t=chaji(p1[size-1],p1[size],p[i]);//算两个凸包点与该点 向量的叉积
        while(t<=0)//叉积<=0  该点不是凸包点
        {
            size--;
            //垂直 或者凸包点全部遍历完
            if(t==0 || size==0) break;
            t=chaji(p1[size-1],p1[size],p[i]);//直到该点遍历完全部凸包点或者 满足>0为凸包点
        }
        p1[++size]=p[i];//记录下一个凸包点
    }
    p1[++size]=p[n-2];
    for(i=n-3;i>=0;i--)//往回找 直到第一个凸包点
    { 
        t=chaji(p1[size-1],p1[size],p[i]);
        while(t<=0)
        {
            size--;
            if(t==0 || size==0) break;
            t=chaji(p1[size-1],p1[size],p[i]);
        }
        p1[++size]=p[i];
    }
}
int main()
{
    int n,i;
    double sum;
    while(~scanf("%d",&n) && n)
    {
        for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
        sort(p,p+n,cmp);
        sovle(n);
        sum=0;
        //计算周长
        for(i=0;i<size;i++) sum=sum+dist(p1[i],p1[i+1]);
        //若只有2个凸包点
        if(size==2) sum=sum/2;
        printf("%.2lf\n",sum);
    }
    return 0;
}

 

posted @ 2016-01-25 12:48  蓦辰  阅读(148)  评论(0编辑  收藏  举报