hdu 4766
先二分答案,然后相当与判断n+1个圆是否有交,注意,只需要判断是否,所以就可以用nlogn的方法来判断。
总体复杂度为nlogn^2,所以跑起来还是很快的
nlogn判断n个圆是否有交是这样的,我们先锁定x的范围,也就是所有圆的右边界的最小值right,以及所有圆的左边界的最大值left
那么n个圆的公共部分肯定在left right之间,而且有一个很重要的性质,如果n个圆的公共部分的左右区间是L,R,假设我们当前枚举的答案是mid,如果x=mid这条直线与所有的圆没有公共部分,那么我们找两个圆与直线的公共部分不相交(其实就是上边界最小以及下边界最大的两个圆),判断这两个圆的公共部分在x=mid的哪一侧即可,其实就是满足二分性质啦~,具体可以看代码
#include <cstdio> #include <cmath> #include <algorithm> using namespace std; const double eps = 1e-8; const double pi = acos(-1.0); struct Point { double x, y; Point() { } Point(double _x,double _y) { x = _x ; y = _y; } Point operator - (const Point& t) const { Point tmp; tmp.x = x - t.x; tmp.y = y - t.y; return tmp; } Point operator + (const Point& t) const { Point tmp; tmp.x = x + t.x; tmp.y = y + t.y; return tmp; } bool operator < (const Point &cmp) const { return x - cmp.x < -eps || (fabs(x-cmp.x)<eps && y -cmp.y < -eps); } bool operator == (const Point& t) const { return fabs(x-t.x) < eps && fabs(y-t.y) < eps; } }GP; struct Line { double a,b,c; }; double PPdis(Point a,Point b) { return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)); } double PL_dis(Point p,Line ln) { return fabs(ln.a*p.x+ln.b*p.y+ln.c) / sqrt(ln.a*ln.a+ln.b*ln.b); } bool Cir_Line(Point ct, double r, Line ln, Point& t1, Point& t2) { // 直线与圆交点 if ( PL_dis(ct, ln) > r + eps ) return false; ln.c += ln.a*ct.x + ln.b*ct.y; if ( fabs(ln.b) < eps ) { t1.x = t2.x = -ln.c/ln.a; t1.y = sqrt(r*r - ln.c*ln.c/ln.a/ln.a); t2.y = -t1.y; } else { double A, B, C; A = ln.a*ln.a + ln.b*ln.b; B = 2.0*ln.a*ln.c; C = ln.c*ln.c - ln.b*ln.b*r*r; t1.x = (-B - sqrt(B*B - 4.0*A*C))/2.0/A; t2.x = (-B + sqrt(B*B - 4.0*A*C))/2.0/A; t1.y = -t1.x*ln.a/ln.b - ln.c/ln.b; t2.y = -t2.x*ln.a/ln.b - ln.c/ln.b; } t1 = t1 + ct; t2 = t2 + ct; return true; } bool Cir_Cir(Point c1, double r1, Point c2, double r2, Point& t1, Point& t2) { // 圆与圆交点 double d = PPdis(c1, c2); if ( d > r1+r2+eps || d < fabs(r1-r2)-eps ) return false; Line ln; ln.a = 2*(c1.x - c2.x); ln.b = 2*(c1.y - c2.y); ln.c = r1*r1 - r2*r2 - (c1.x*c1.x+c1.y*c1.y-c2.x*c2.x-c2.y*c2.y); Cir_Line(c1, r1, ln, t1, t2); return true; } double R[1010]; Point p[1010]; int n; bool judge(double mid) { double Left,Right; for(int i = 0; i <= n; i++) { if(i == 0) { Left = p[i].x - R[i]; Right = p[i].x + R[i]; } else{ if(p[i].x-R[i] > Left) Left = p[i].x-R[i]; if(p[i].x+R[i] < Right) Right = p[i].x+R[i]; } } if(Left - Right > eps) return false; int step = 50; while(step--) { double mid = (Left + Right)*0.5; double low,high,uy,dy; int low_id,high_id; for(int i = 0; i <= n; i++) { double d = sqrt(R[i]*R[i]-(p[i].x-mid)*(p[i].x-mid)); uy = p[i].y + d; dy = p[i].y - d; if(i == 0) { low_id = high_id = 0; low = dy; high = uy; } else { if(uy < high) high = uy,high_id = i; if(dy > low) low = dy,low_id = i; } } if(high - low > -eps) { return 1; } Point a,b; if(Cir_Cir(p[high_id],R[high_id],p[low_id],R[low_id],a,b)) { if((a.x+b.x)*0.5 < mid) { Right = mid; } else Left = mid; } else return false;
} return false; } int main() { Point p0; double d; while(scanf("%lf%lf",&p0.x,&p0.y)!=EOF) { scanf("%lf",&d); scanf("%d",&n); for(int i = 0; i < n; i++) { R[i] = d; scanf("%lf%lf",&p[i].x,&p[i].y); } p[n] = p0; double Left = 0, Right = 1e10, best = -1; int step = 50; while(step--) { double mid = (Left + Right)*0.5; R[n] = mid; if(judge(mid)) { best = mid; Right = mid; } else Left = mid; } if(best == -1) { puts("X"); }else printf("%.2f\n",best); } }