题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1392
这里介绍一种求凸包的算法:Graham。(相对于其它人的解释可能会有一些出入,但大体都属于这个算法的思想,同样可以解决凸包问题)
相对于包裹法的n*m时间,Graham算法在时间上有很大的提升,只要n*log(n)时间就够了。它的基本思想如下:
1、首先,把所有的点按照y最小优先,其次x小的优先排序
2、维护一个栈,用向量的叉积来判断新插入的点跟栈顶的点哪个在外围,如果栈顶的点在当前插入的点的左边,那么把栈顶的这个元素弹出,弹出之后不能继续插入下一个点,要继续判断当前插入点跟弹出之后的栈顶的点的位置关系,当当前插入的点在栈顶的那个点的左边时,则可以将要插入的点压到栈中,进入下一个点。
对于这题,最后要计算的是凸包的边长,所以最后别忘了加上最后一个点到第一个点的距离。还有只有一个点和两个点的情况时要进行特判。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int maxn = 105; 8 struct point 9 { 10 double x,y; 11 point(double x = 0,double y = 0):x(x),y(y) {} 12 friend point operator + (point p1,point p2) 13 { 14 return point(p1.x+p2.x,p1.y+p2.y); 15 } 16 friend point operator - (point p1,point p2) 17 { 18 return point(p1.x-p2.x,p1.y-p2.y); 19 } 20 21 }p[maxn],res[maxn]; 22 double dis(point p1,point p2) 23 { 24 return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y)); 25 } 26 double dot(point p1,point p2) 27 { 28 return p1.x*p2.y - p2.x*p1.y; 29 } 30 bool cmp(point p1,point p2) 31 { 32 if(p1.y == p2.y) return p1.x < p2.x; 33 return p1.y < p2.y; 34 } 35 int graham(point* p,int n,point* res) 36 { 37 sort(p,p+n,cmp); 38 res[0] = p[0]; 39 res[1] = p[1]; 40 // res[2] = p[2]; 41 int top = 1,len; 42 for(int i = 2;i < n;++i) 43 { 44 while(top && dot(p[i]-res[top-1],res[top]-res[top-1]) >= 0) top--; 45 res[++top] = p[i]; 46 } 47 len = top; 48 for(int i = n-1;i >= 0;--i) 49 { 50 while(top != len && dot(p[i]-res[top-1],res[top]-res[top-1]) >= 0) top--; 51 res[++top] = p[i]; 52 } 53 return top; 54 } 55 56 int main() 57 { 58 // freopen("in.txt","r",stdin); 59 int n; 60 while(scanf("%d",&n),n) 61 { 62 for(int i = 0;i < n;++i) 63 scanf("%lf%lf",&p[i].x,&p[i].y); 64 if(n == 1) 65 { 66 printf("0.00 "); 67 continue; 68 } 69 if(n == 2) 70 { 71 printf("%.2lf ",dis(p[0],p[1])); 72 continue; 73 } 74 int m = graham(p,n,res); 75 double tot = 0; 76 for(int i = 1;i <= m;++i) 77 tot += dis(res[i-1],res[i]); 78 printf("%.2lf ",tot); 79 } 80 return 0; 81 }