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 }