poj 1113 Wall(经典凸包)
据说这题是经典的凸包题,可以直接套模板,由于刚开始看凸包,因此以它为例题,熟悉一下凸包模板~
点集Q的凸包是一个最小的凸多边形P,满足Q中的每个点或者在P的边界上,或者在P的内部,通俗点讲就是,一块木板上有许多铁钉,而凸包就是要求包围了所有这些铁钉的一条拉紧了的橡皮绳所构成的形状。
求凸包的一种比较常用的方法的Graham扫描法,其步骤为:
步骤1:以y轴最低点为基点,找到基点p0。
步骤2:以基点p0为一个坐标系的远点,求各点与基点p0的极角,并一次从小到大排序
步骤3:按照顺序,每个点都要判断与其前面的点构成的两条线段转向问题(左转还是右转?用叉积判断)。
步骤4:依次下去,知道所有的点结束,就完成的凸包的寻找。
个人觉得下面的博客讲的很好:
http://www.wutianqi.com/?p=2622
如果想深入学习的,建议看看算法导论,上面有相关Graham方法的证明。
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> #include <math.h> #define N 1004 #define pi acos(-1.0) using namespace std ; struct node { int x , y ; }p[N] , q[N] ; int n , l , top ; //求两点间的距离 double dis ( struct node p1 , struct node p2 ) { double d = sqrt((double)(( p1.x - p2.x ) * ( p1.x - p2.x ) + ( p1.y - p2.y ) * ( p1.y - p2.y ))); return d ; } //求线段p0p1 与p0p2 的相对位置 double cross ( struct node p1 , struct node p2 , struct node p0 ) { double sum = ( p1.x - p0.x ) * ( p2.y - p0.y ) - ( p2.x - p0.x ) * ( p1.y -p0.y ); return sum ; } //用sort进行排序 int cmp ( struct node p1 , struct node p2 ) { if ( cross ( p1 , p2 , p[0] ) > 0 ) return 1 ; if ( cross ( p1 , p2 , p[0] ) == 0 && dis ( p1 , p[0] ) < dis ( p2 ,p[0] )) return 1 ; return 0 ; } void Graham () { int i ,j , k ; k = 0 ; for ( i = 1 ; i < n ; i++ ) if ( ( p[i].y < p[k].y ) || ( p[i].y == p[k].y && p[i].x < p[k].x )) k = i ; struct node tem = p[0] ; p[0] = p[k] ; p[k] = tem ; sort ( p , p + n , cmp ); top = 0 ; q[top++] = p[0] ; q[top++] = p[1] ; q[top++] = p[2] ; for ( i = 3 ; i < n ; i++ ) { while ( cross ( p[i] , q[top-1] , q[top-2] ) >= 0 ) top-- ; q[top++] = p[i] ; } } int main() { int i ; while ( scanf ( "%d%d" , &n , &l ) != EOF ) { for ( i = 0 ; i < n ; i++ ) scanf ( "%d%d" , &p[i].x , &p[i].y ); Graham(); double ans = 0.0 ; for ( i = 0 ; i < top ; i++ ) { ans += dis( q[i] , q[(i+1)%top] ); } ans += 2 * pi * l ; printf ( "%d\n" , (int)( ans +0.5 )); } return 0 ; }