UVa 10135 - Herding Frosh

来源:互联网 发布:联通云数据 待遇 编辑:程序博客网 时间:2024/04/29 14:43

题目:在平面上有一些点,从(0,0)出发,用一条丝带把所有的点都围起来,问最小长度。

分析:计算几何、凸包。求(0,0)与n个点的凸包,可分为两种情况:1.(0,0)在凸包上;2.(0,0)在凸包内。如下图:

            图1:                图2:

            其中黄色的点就是(0,0)。对于情况1直接输出凸包的周长即可;对于情况2有点复杂,假设图2里面的部分就是我们要求的解,那么我们可以得到下面的结论。如下图:

            图3:        图4:

            这里过(0,0)的直线,将点分成左右两个集合,其中直线上的点为一个集合所有,图3或图4两种情况(当图3向顺时针旋转就是图4,所以不必重复计算),黄色和绿色的点为共有,他们分别构成凸包,注意这里新加入了一个绿色节点,这是直线与凸包的交点。因此,我们这里枚举所有点和(0,0)构成的直线,求解两个凸包的周长和减掉黄色点和绿色点间的线段长度,求出最小值即可(这种情况可以包含情况1,不过特判一下可以提高效率)。这里还有一个优化(我的代码中没有使用),即每次只处理(0,0)与凸包相邻顶点的三角形的点,方法一样,不过这么分治了一下总体的代价会小很多。

#include <algorithm>#include <iostream>#include <cstdlib>#include <cstdio>#include <cmath>using namespace std;//点结构 typedef struct pnode{double x,y,d;pnode( double a, double b ){x = a;y = b;}pnode(){}}point;point P0,D[1005],C[1005];point S1[1005],S2[1005];//线结构 typedef struct lnode{double x,y,dx,dy;lnode( point a, point b ){x = a.x;y = a.y;dx = b.x-a.x;dy = b.y-a.y;}lnode(){}}line; //叉乘ab*ac double crossproduct( point a, point b, point c ){return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);}//点到点距离 double dist( point a, point b ){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}//坐标排序 bool cmp1( point a, point b ){return (a.x==b.x)?(a.y<b.y):(a.x<b.x);}//级角排序 bool cmp2( point a, point b ){double cp = crossproduct( P0, a, b );if ( cp == 0 ) return a.d < b.d;else return cp > 0;}//计算凸包 double Graham( point *P, int n, int &m ){if ( n < 1 ) return 0;sort( P+0, P+n, cmp1 );for ( int i = 1 ; i < n ; ++ i )P[i].d = dist( P[0], P[i] );P0 = P[0];sort( P+1, P+n, cmp2 );int top = n-1;if ( n > 2 ) {top = 1;for ( int i = 2 ; i < n ; ++ i ) {while ( top > 0 && crossproduct( P[top-1], P[top], P[i] ) <= 0 ) -- top;P[++ top] = P[i];}}P[++ top] = P[0];m = top;double sum = 0.0;for ( int i = 0 ; i < top ; ++ i )sum += dist( P[i], P[i+1] );return sum;}//点与直线关系 double to( point p, line l ){return l.dx*(p.y-l.y)-l.dy*(p.x-l.x);}//点在线段上 bool on( point p, line l ){if ( l.dx*(p.y-l.y)-l.dy*(p.x-l.x) == 0 )if ( (p.x-l.x)*(p.x-l.x-l.dx) <= 0 )if ( (p.y-l.y)*(p.y-l.y-l.dy) <= 0 )return true;return false;}//点在多边形上bool on( point p, point* P, int n ){for ( int i = 0 ; i < n ; ++ i ) {line s = line( P[i], P[i+1] );if ( on( p, s ) ) return true;}return false;}//直线与线段相交 bool cross( line a, point b, point c ){double t1 = 0.0+a.dx*(b.y-a.y)-a.dy*(b.x-a.x);double t2 = 0.0+a.dx*(c.y-a.y)-a.dy*(c.x-a.x);return t1*t2 <= 0;}//两直线交点point crosspoint( line l, point a, point b )  {      line m = line( a, b );      if ( m.dx*l.dy == m.dy*l.dx ) {          if ( dist( point( l.x, l.y ), a ) < dist( point( l.x, l.y ), b ) )              return a;          else return b;      }else {          double a1 = -l.dy,b1 = l.dx,c1 = l.dx*l.y-l.dy*l.x;          double a2 = -m.dy,b2 = m.dx,c2 = m.dx*m.y-m.dy*m.x;          double x = (c1*b2-c2*b1)/(a1*b2-a2*b1);          double y = (c1*a2-c2*a1)/(b1*a2-b2*a1);          return point( x, y );      }   }  //求直线和凸多边形交点 point cross( line l, point* P, int n ){for ( int i = 0 ; i < n ; ++ i )if ( cross( l, P[i], P[i+1] ) ) {point p = crosspoint( l, P[i], P[i+1] );if ( p.x == l.x && p.y == l.y ) continue;if ( on( point(0,0), line( p, point( l.x, l.y ) ) ) )return p;}return point(0,0);}int main(){int T,N,M,O;while ( scanf("%d",&T) != EOF ) for ( int t = 1 ; t <= T ; ++ t ) {scanf("%d",&N);for ( int i = 0 ; i < N ; ++ i ) {scanf("%lf%lf",&D[i].x,&D[i].y);C[i].x = D[i].x;C[i].y = D[i].y;}C[N].x = C[N].y = 0.0;double ans = Graham( C, N+1, M );if ( !on( point(0,0), C, M ) ) {ans += 2.0*dist( point(0,0), C[0] );for ( int i = 0 ; i < N ; ++ i ) {if ( !D[i].x && !D[i].y ) continue;//构造直线将点分成两个集合line  l = line( D[i], point(0,0) );//计算直线与凸包交点 point p = cross( l, C, M );int   s1_count = 0,s2_count = 0;for ( int j = 0 ; j < N ; ++ j ) {double ptol = to( D[j], l );if ( ptol <  0 ) S1[s1_count ++] = D[j];if ( ptol >= 0 ) S2[s2_count ++] = D[j]; }S1[s1_count ++] = point(0,0); S1[s1_count ++] = p;S2[s2_count ++] = point(0,0); S2[s2_count ++] = p;double ans1 = Graham( S1, s1_count, O )+Graham( S2, s2_count, O )-2.0*dist( point(0,0), p );if ( ans > ans1 ) ans = ans1;}}printf("%.2lf\n",ans+2.0);if ( t != T ) printf("\n");}return 0;}