HDOJ1392 Surround the Trees

 

  原题传送:http://acm.hdu.edu.cn/showproblem.php?pid=1392

 

  以下内容转自  http://dev.gameres.com/Program/Abstract/Geometry.htm#凸包的求法

 

  求凸包。

凸包的概念:
 
  点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内。下图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。

 
凸包的求法:
 
  现在已经证明了凸包算法的时间复杂度下界是O(n*logn),但是当凸包的顶点数h也被考虑进去的话,Krikpatrick和Seidel的剪枝搜索算法可以达到O(n*logh),在渐进意义下达到最优。最常用的凸包算法是Graham扫描法和Jarvis步进法。本文只简单介绍一下Graham扫描法,其正确性的证明和Jarvis步进法的过程大家可以参考《算法导论》。
 
 对于一个有三个或以上点的点集Q,Graham扫描法的过程如下:
 
 令p0为Q中Y-X坐标排序下最小的点 
 设<p1,p2,...pm>为对其余点按以p0为中心的极角逆时针排序所得的点集(如果有多个点有相同的极角,除了距p0最远的点外全部移除)
  压p0进栈S
  压p1进栈S
  压p2进栈S
    for i ← 3 to m
      do while 由S的栈顶元素的下一个元素、S的栈顶元素以及pi构成的折线段不拐向左侧
        对S弹栈
      压pi进栈S
    return S;
 
  此过程执行后,栈S由底至顶的元素就是Q的凸包顶点按逆时针排列的点序列。需要注意的是,我们对点按极角逆时针排序时,并不需要真正求出极角,只需要求出任意两点的次序就可以了。而这个步骤可以用矢量叉积性质实现。(ps:其实凸包顺时针扫描或者逆时针扫描依据个人爱好决定,不同部分只需要改动排序顺序和矢量叉积即可)
View Code
 1 /*
 2     Author: bcegkmqsw
 3 */
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <cmath>
 7 
 8 const double eps = 1e-8;
 9 
10 struct Point
11 {
12     double x, y;
13 }points[101], res[101];
14 
15 inline int dcmp(double d) // 浮点误差处理
16 {
17     if(fabs(d) < eps) return 0;
18     return d > 0 ? 1 : -1;
19 }
20 
21 inline double det(double x1, double y1, double x2, double y2)  // 计算叉积
22 {
23     return x1 * y2 - x2 * y1;
24 }
25 
26 inline double cross(Point a, Point b, Point c)  // 右手螺旋为正
27 {
28     return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
29 }
30 
31 int cmp(const void *a, const void *b)   // Y-X型坐标排序
32 {
33     
34     if(!dcmp((*(Point *)a).y - (*(Point *)b).y))
35         return dcmp((*(Point *)a).x - (*(Point *)b).x);
36     return dcmp((*(Point *)a).y - (*(Point *)b).y);
37 }
38 
39 int Graham(Point p[], Point res[], int n, int &top)  // 求凸包,结果为逆时针顺序
40 {
41     int len, i;
42     top = 1;
43     qsort(p, n, sizeof(Point), cmp);
44     res[0] = p[0], res[1] = p[1];
45     for(i = 2; i < n; i ++)
46     {
47         while(top && cross(res[top], res[top - 1], p[i]) <= 0) // 不拐向右侧就出栈(求出从底到顶,凸包左半部分)
48             -- top;
49         res[++ top] = p[i];
50     }
51     len = top;
52     res[++ top] = p[n - 2];
53     for(i = n - 3; i >= 0; -- i)
54     {
55         while(top != len && cross(res[top], res[top - 1], p[i]) <= 0) // 不拐向右侧就出栈(求出从顶到底,凸包右半部分)
56             -- top;
57         res[++ top] = p[i];
58     }
59     return top;
60 }
61 
62 int main()
63 {
64     int n, i, top;
65     Point tmp;
66     double len;
67     while(scanf("%d", &n), n)
68     {
69         len = 0.0;
70         for(i = 0; i < n; i++)
71             scanf("%lf%lf", &points[i].x, &points[i].y);
72         if(n == 1)
73         {
74             printf("%.2lf\n", len);
75             continue;
76         }
77         else if(n == 2)
78         {
79             len = sqrt((points[1].x - points[0].x) * (points[1].x - points[0].x) + (points[1].y - points[0].y) * (points[1].y - res[0].y));
80             printf("%.2lf\n", len);
81             continue;
82         }
83         
84         Graham(points, res, n, top);
85         
86         Point a, b;
87         
88         for(i = top; i > 1; i --)
89         {
90             a = res[i], b = res[i - 1];
91             len += sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
92         }
93         len += sqrt((res[1].x - res[top].x) * (res[1].x - res[top].x) + (res[1].y - res[top].y) * (res[1].y - res[top].y));
94         printf("%.2lf\n", len);
95     }
96     return 0;
97 }
posted @ 2012-09-14 18:06  芒果布丁  阅读(190)  评论(0编辑  收藏  举报