凸包学习小结

这两天翻看了些许凸包的资料,在此留下学习脚印……

凸包是啥呢……问度娘 = =、

大家都知道的就不说了,很多资料都写了,而且目测写得也比我好多了吧……

 

常用算法:枚举法 O(n ^ 3)的复杂度 , Graham扫描法 , 分治法(不懂....)

 

让我兴奋的是,另一种网上资料难以找到的Graham扫描算法的变种:(LRJ训练指南上称作:Andrew算法)

LRJ注释上写此种算法更快,且数值稳定性更好……故很想一试,奈何神牛就是神牛,他的介绍文字加起来不过1页……让我这个原来连凸包是什么都不懂的菜鸟鸭梨山大啊……

后来,在捉题目的时候,翻看某博主的文章,里面写的与Graham算法完全不同,因为他不用将点按极角(幅角)排序,而是直接对点按y坐标的升序排列,让我眼前一亮。

说真的,Graham算法在判断平角时还是比较麻烦的……而Andrew算法正好回避了这个问题…

果断照着打了一遍,在通过白书和自己模拟后,也明白了些许:

算法流程:

1、将点按y坐标升序排列(y相同的按x升序)

2、将点p[0] , p[1]入栈,指针top指向1

3、判断三角区符号,如果为负,说明当前栈针所指点不满足凸包上的点,则将点退栈

4、(3)过程所得是“上凸包”,我们再反过来从p[n]开始,做一次,得到“下凸包”

5、两次所得(此时栈中的点)就是完整的凸包了

 

补充三角区符号知识:

至于这个算法的几点思考:

1、为什么三角区函数为负,则说明前点不属于凸包?

理解:因为新入的点不能在原来两点所成向量的外侧(凸包是要将所有点都包含在里面),因此必须要满足,不满足说明当前点不能属于凸包上的点,需退栈!

 

2、这个算法怎么处理平角(也就是三角区函数为0)的情况?

 解答:这种叉积形式已经包含了这个情况!

------------------------------分割线---------------------------------------

最后附上例题一枚:HDU 1392  Surround the Trees

附上小人的代码,大神勿喷啊……

View Code
#include <cstdio>
#include <cmath>
#include <algorithm>

struct point
{
    int x , y;
}p[110] , chn[110];


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

bool xmult (point p1 , point p2 , point p3)
{
     return ((p2.x - p1.x) * (p3.y - p1.y) > (p2.y - p1.y) * (p3.x - p1.x));
}

int andrew(int n)
{
    int len , top = 1;
    chn[0] = p[0];
    chn[1] = p[1];
    for (int i = 2 ; i < n ; i ++)
    {
        while (top && !xmult(chn[top] , chn[top - 1] , p[i])) top --;
        chn[++ top] = p[i];
    }
    len = top;
    chn[++ top] = p[n - 2];
    for (int i = n - 3 ; i >= 0 ; i --)
    {
        while (top != len && !xmult(chn[top] , chn[top - 1] , p[i])) top --;
        chn[++ top] = p[i];
    }
    return top;
}

double dis(point a , point b)
{
       return (sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
}
        
int main()
{
    int n;
    while(scanf("%d" , &n) , n != 0)
    {
        for (int i = 0 ; i < n ; i ++)
          scanf("%d%d" , &p[i].x , &p[i].y);
        std::sort(p , p + n , cmp);
        int top = andrew(n);
        double ans = 0.0;
        for (int i = 0 ; i < top ; i ++)
          ans += dis(chn[i % top] , chn[(i + 1) % top]);
        if (top == 2) ans /= 2;
        printf("%.2lf\n" , ans);
    }
    return 0;
}

 

posted on 2013-02-05 15:35  Hmm  阅读(470)  评论(0编辑  收藏  举报

导航