标签:
Description
Input
Output
Sample Input
Sample Output
Hint
Please see the sample picture for more details, the fence is highlighted with red.
题意:给定n个圆,m个三角形,求凸包的周长,详见上图。
题解:这个题有一种很水的做法就是把圆分成1000个点,然后直接对这些点求凸包。
不推荐这个方法,属于水过的,换个精度高的数据没准就WA了,正确做法如下:
把三角形的所有点视为单个点,放到一个集合P中,求这些点与圆的切线的交点,
把这些点再放到集合P中。再求圆与圆之间的内切线外切线与圆的交点,再放到
集合P中,把这些点进行凸包就可以求出周长了。
有些细节问题需要注意,已写到注释里。
另外注意在没有三角形只有一个圆的情况下要单独考虑,因为不会通过点和圆之间
的切线以及圆和圆之间的切线产生点,所以要单独判断然后输出圆的面积,
continue即可。
对于数组为什么开2W,目前未知,求大神解答。
#include <iostream> #include <math.h> #include <stdio.h> #include <string.h> #include <algorithm> #include <stdlib.h> #include <vector> const double PI=acos(-1.0); using namespace std; struct Point{ double x,y; int id; Point(double x=0,double y=0,int id=-1):x(x),y(y){} //构造函数,方便代码编写 }; typedef Point Vector; //从程序上实现,Vector只是Point的别名 struct Circle{ Point c; double r; Circle() {} Circle(Point c,double r):c(c),r(r){} Point point(double a){ return Point(c.x+cos(a)*r,c.y+sin(a)*r); } }; //定义 #define N 50000 //数组最小开20000 为什么开20000没算对 //我算的是 150+300+C(50,2)*4 即三角形的点+这些点与圆的切点+圆之间的切点 不到6000 //开大点就完了 开500000 才 42344KB 上限65536 够用了 Point p[N]; Point ch[N]; Circle c[N]; Point a[10],b[10]; Point q[N]; int m,n,t; //点-点=向量 Vector operator - (Point A,Point B) { return Vector(A.x-B.x,A.y-B.y); } //运算符重载 bool operator <(const Point &a,const Point &b) { return a.x<b.x||(a.x==b.x&&a.y<b.y); } const double eps=1e-10; //三态函数精度问题 int dcmp(double x) { if(fabs(x)<eps) return 0; else return x<0?-1:1; } bool operator ==(const Point &a,const Point &b) { return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0; } double Dot(Vector A,Vector B) { return A.x*B.x+A.y*B.y; } //求向量的值 double Length(Vector A) { return sqrt(Dot(A,A)); } //求夹角 double Angle(Vector A,Vector B) { return acos(Dot(A,B)/Length(A)/Length(B)); } //叉积 double Cross(Vector A,Vector B) { return A.x*B.y-A.y*B.x; } //求凸包模板 int ConvexHull(Point *p,int n,Point* ch) //注意是*ch { sort(p,p+n); //这个要用到<重载运算符 n=unique(p,p+n)-p; //这个要用到==重载运算符 int m=0; for(int i=0;i<n;i++) { //注意:可以共线时“<=”改为“<” 为什么 //上面那句话是百度的 个人感觉他说反了 while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--; ch[m++]=p[i]; } int k=m; for(int i=n-2;i>=0;i--) //注意是--不是++ { while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--; ch[m++]=p[i]; } if(n>1) m--; return m;//别忘了加m } Point readpoint() { double x,y; scanf("%lf%lf",&x,&y); return Point(x,y); } void readcircle(Circle &c) { scanf("%lf%lf%lf",&c.c.x,&c.c.y,&c.r); } //点到圆的切线 v存的是切点 int pcl(Point p,Circle o,Vector* v)//点到圆的切线的切点,考虑不同情况 { Point u=p-o.c; double d=Length(u); double a=atan2(u.y,u.x); if(d<o.r)return 0;//园内 else if(dcmp(d-o.r)==0)//圆上 { v[0]=o.point(a); return 1; } else { double ang=acos(o.r/d); v[0]=o.point(a+ang); v[1]=o.point(a-ang); return 2; } } //两圆相离的时候的外公共切线 int ccl(Circle c1,Circle c2,Point *a,Point *b) { int cnt=0; if(dcmp(c1.r-c2.r)<0)//保证结果为正值 { swap(c1,c2); swap(a,b); } Vector u=c2.c-c1.c; double d=Length(u); double ang=atan2(u.y,u.x); //有外公切线 double g=acos((c1.r-c2.r)/d); a[cnt]=c1.point(ang+g); b[cnt]=c2.point(ang+g); cnt++; a[cnt]=c1.point(ang-g); b[cnt]=c2.point(ang-g); cnt++; return cnt; } double arc(Point a,Point b,Circle c)//a,b逆时针穿过圆c外面的的圆弧长 { Point u=a-c.c,v=b-c.c; double ang=Angle(u,v); //Angle 是夹角 angle是极角 if(Cross(u,v)>eps) return ang*c.r; return (PI*2.0-ang)*c.r; } int main() { while(scanf("%d%d",&n,&m)==2) { m=m*3; for(int i=0; i<n; i++) //圆 readcircle(c[i]); for(int i=0; i<m; i++) //三角形 { p[i]=readpoint(); p[i].id=-i*3-1; } if(n==1&&!m) { printf("%.5f\n",PI*2*c[0].r);; //输出一个圆的面积 continue; } // 注意 swap是把数值换位置 n和m不换位置 for(int i=m-1; i>=0; i--) for(int j=0; j<n; j++) { Point v[2]; //存放切点 int num=pcl(p[i],c[j],v); //点到圆的切线 p是点 c是圆 v是切点 num是切点个数 for(int k=0; k<num; k++) { p[m++]=v[k];//把切点放入点集中 p[m-1].id=j;//给新的点做标记 这个点是第j个圆的 } } for(int j=0; j<n; j++) for(int i=j+1; i<n; i++) { int num=ccl(c[j],c[i],a,b); //圆和圆之间的外切点 for(int k=0; k<num; k++) { p[m++]=a[k]; p[m-1].id=j; p[m++]=b[k]; p[m-1].id=i; } } m=ConvexHull(p,m,ch); memcpy(p,ch,sizeof(ch)); double len=0.0; p[m]=p[0]; for(int i=0; i<m; i++) { if(p[i].id==p[i+1].id) //如果标记相等说明在一个圆上 { len+=arc(p[i],p[i+1],c[p[i].id]); //两个点绕圆弧逆时针的长 } else { len+=Length(p[i]-p[i+1]);//不在一个圆上输出算直线距离 } } printf("%.5f\n",len); } return 0; }
HDU 4667 Building Fence(求凸包的周长)
标签:
原文地址:http://www.cnblogs.com/Ritchie/p/5509803.html