问题描述
有很多树在一个区域。 一个农民想买一根绳子环绕这些树木。 首先他必须知道所需的最小长度的绳子。 然而,他不知道如何计算它。 你能帮助他吗?
树木的直径和长度都省略了,这意味着树可以被视为一个点。 绳子的厚度也省略了这意味着一根绳子可以被看作是一条直线.没有超过100棵树。
树木的直径和长度都省略了,这意味着树可以被视为一个点。 绳子的厚度也省略了这意味着一根绳子可以被看作是一条直线.没有超过100棵树。
输入
输入包含一个或多个数据集。 每个输入的第一行数据集数量的树木在这个数据集,它是紧随其后的是一系列的坐标的树。 每一对坐标是一个正整数,每个整数小于32767。 每一对由空格分隔。
零数量的树木终止程序的输入。
零数量的树木终止程序的输入。
输出
最小长度的绳子。 精度应该10 ^ -2。
Sample Input
9 12 7 24 9 30 5 41 9 80 7 50 87 22 9 45 1 50 7 0
Sample Output
243.06
所谓凸包,在《算法导论
》第599页有这样的解释:多边形是平面上由一系列线段构成的闭合曲线。也就是说,它由一系列直线段构成的首尾相连的曲线。这些直线段称为多边形的边。一个连接两条连续边的顶点称为多边形的顶点。如果多边形是简单的,那么它的内部不存在边交叉的情况。在平面上被简单多边形包围的点集组成了该多边形的内部,恰落在多边形上的点组成了多边形的边界,而包围该多边形的点构成 了多边形的外部。对于一个简单多边形,如果给定任意两个位于其边界或内部的点,连接这两个点的线段上的所有点都在这个多边形的边界或内部,那么该多边形为凸多边形。
了解完凸包,我们就需要知道如何求得凸包,所以在这里我用到的是Graham算法。
在《算法导论》中对Graham算法有如下描述:
Graham扫描算法:
Graham扫描法通过维持一个关于候选点的栈S来解决凸包问题。输入集合Q中的每个点都被压入栈一次,非CH(Q)中顶点的点最终被弹出栈,当算法终止时,栈S中仅包含CH(Q)中的顶点,以逆时针的顺序出现在边界上。
过程的输入为点集Q,其中|Q|>=3.在无需改变栈S的情况下,调用函数TOP(S)和函数分别返回处于栈顶的点和处于栈顶部下面的那个点。
返回的是栈S从底部到顶部,依次是按逆时针方向排列的CH(Q)中的顶点。
下面是Graham 算法伪代码
Graham-scan(Q)
let p0 be the point in Q with the minimum y-coordinate,
or the leftmost such point in case of a tie
let (p1,p2,...pm) be the remaining points in Q,
sorted by polar angle in counterclockwise order around p0
(if more than one point has the same angle ,remove all but
the one that is farthest from p0)
if(m<2)
return "convex hull is empty"
else let S be an empty stack
PUSH(p0,S)
PUSH(p1,S)
PUSH(p2,S)
for i=3 to m
while the angle formed by points NEXT-TO-TOP(S),TOP(S),
and pi makes a nonleft turn
POP(S)
PUSH(pi,S)
return S
下面给出图示:
AC代码:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
typedef struct
{
double x , y ;
}POINT ;
POINT result[110] ;// 模拟堆栈S,保存凸包上的点
POINT tree[110] ;
int n , top ;
double Distance ( POINT p1 , POINT p2 )
{
return sqrt( (p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y) ) ;
}
double Multiply(POINT p1 , POINT p2 , POINT p3) // 叉积
{
return ( (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x) ) ;
}
int cmp ( const void *p1 , const void *p2 )
{
POINT *p3,*p4;
double m;
p3 = (POINT *)p1;
p4 = (POINT *)p2;
m = Multiply(tree[0] , *p3 , *p4) ;
if(m < 0) return 1;
else if(m == 0 && (Distance(tree[0] , *p3) < Distance(tree[0],*p4)))
return 1;
else return -1;
}
void Tubao ()
{
int i ;
result[0].x = tree[0].x;
result[0].y = tree[0].y;
result[1].x = tree[1].x;
result[1].y = tree[1].y;
result[2].x = tree[2].x;
result[2].y = tree[2].y;
top = 2;
for ( i = 3 ; i <= n ; ++ i )
{
while (Multiply(result[top - 1] , result[top] , tree[i]) <= 0 )
top -- ; //出栈
result[top + 1].x = tree[i].x ;
result[top + 1].y = tree[i].y ;
top ++ ;
}
}
int main ()
{
int pos ;
double len , temp , px , py ;
while ( scanf ( "%d" , &n ) != EOF , n )
{
py = -1 ;
for ( int i = 0 ; i < n ; ++ i )
{
scanf ( "%lf%lf" , &tree[i].x , &tree[i].y ) ;
}
if ( n == 1 )
{
printf ( "0.00\n" ) ;
continue ;
}
else if ( n == 2 )
{
printf ( "%.2lf\n" , Distance(tree[0] , tree[1]) ) ;
continue ;
}
for ( int i = 0 ; i < n ; ++ i )
{
if(py == -1 || tree[i].y < py)
{
px = tree[i].x;
py = tree[i].y;
pos = i;
}
else if(tree[i].y == py && tree[i].x < px)
{
px = tree[i].x;
py = tree[i].y;
pos = i;
}
}
temp = tree[0].x ; // 找出y最小的点
tree[0].x = tree[pos].x ;
tree[pos].x = temp ;
temp = tree[0].y ;
tree[0].y = tree[pos].y ;
tree[pos].y = temp ;
qsort(&tree[1],n - 1,sizeof(double) * 2,cmp);
tree[n].x = tree[0].x;
tree[n].y = tree[0].y;
Tubao();
len = 0.0;
for(int i = 0 ; i < top ; i ++)
len = len + Distance(result[i] , result[i+1]) ;
printf("%.2lf\n",len);
}
return 0 ;
}
题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=1392