Graham扫描法

首先先介绍一下叉乘

记P1=(x1,y1,z1),P2=(x2,y2,z2)
这里写图片描述
接下来进行Graham时,z1=z2=0,所以只剩下(x1*y2-x2*y1)k,当x1*y2-x2*y1>0时,即结果与k同向,所以P1在P2的右侧,=0为共线,<0为P1在P2左侧。

接下来推荐Graham讲解的博客
http://blog.csdn.net/u012328159/article/details/50808360
http://blog.csdn.net/tmljs1988/article/details/7259331

附模板:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 1005;
const double PI = acos(-1.0);

struct Point
{
    int x, y;
    Point(int x=0,int y=0):x(x),y(y){}
}list[MAXN];

int stack[MAXN], top, lenth, n;

void swap(int i, int j)
{
    Point tmp;
    tmp = list[i];
    list[i] = list[j];
    list[j] = tmp;
}

int cross(Point p0, Point p1, Point p2)//叉乘,以p0为起点
{
    return (p1.x - p0.x)*(p2.y - p0.y) - (p1.y - p0.y)*(p2.x - p0.x);
}

double dis(Point p1, Point p2)//两点距离
{
    return sqrt((double)(p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y)*(p2.y - p1.y));
}

bool cmp(Point p1, Point p2)//排序,极角小的在前,当极角相同时,离极坐标原点近的在前
{
    int tmp = cross(list[0], p1, p2);
    if (tmp>0)
        return true;
    else if (tmp == 0 && dis(list[0], p1)<dis(list[0], p2))
        return true;
    else
        return false;
}

void init()
{
    int k = 0;
    for (int i = 1;i<n;i++)
        if ((list[k].y>list[i].y) || ((list[k].y == list[i].y) && (list[k].x>list[i].x)))
            k = i;//找出y最小的点作为极坐标原点,y相同找x最小
    swap(0, k);//list[0]就是极坐标原点
    sort(list + 1, list + n, cmp);
}

void graham()
{
    if (n == 1)
    {
        top = 0;
        stack[0] = 0;
    }
    else
    {
        for (int i = 0;i <= 1;i++)
            stack[i] = i;
        top = 1;
        for (int i = 2;i < n;i++)
        {
            while (top>0 && cross(list[stack[top - 1]], list[i], list[stack[top]]) >= 0)
                top--;
            stack[++top] = i;
        }
    }
    lenth = top + 1;
}

double  periMeter()//凸包的周长
{
    if (lenth == 1) return 0;
    double res = 0;
    for (int i = 0;i < lenth;i++)
        res += dis(list[stack[i]], list[stack[(i + 1) % lenth]]);
    return res;
}

double getArea()//凸包的面积
{
    if (lenth <= 2) return 0;
    double area = 0;
    for (int i = 2;i < lenth;i++)//叉乘为两个向量构成的平行四边形的面积,所以最后要除以2
        area += cross(list[stack[0]], list[stack[i - 1]], list[stack[i]]);
    return fabs(area) / 2;
}

int main()
{
    list[0] = Point(1, 0);
    list[1] = Point(2, 0);
    list[2] = Point(0, 1);
    list[3] = Point(0, 2);
    list[4] = Point(1, 1);
    list[5] = Point(2, 1);
    list[6] = Point(3, 1);
    list[7] = Point(3, 2);
    list[8] = Point(2, 3);
    list[9] = Point(1, 2);
    n = 10;
    init();
    graham();
    for (int i = 0;i < lenth;++i)
        printf("x:%d,y:%d\n", list[stack[i]].x, list[stack[i]].y);
    printf("%f\n", periMeter());
    printf("%f\n", getArea());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值