题意是有n个人,每个人有各自坐标以及速度,求一个点X,使得所有点走到X点,耗时最大的那个人的耗时最小。输出这个时间。
抽象成圆的面积交: 二分枚举时间将求极值问题转换为判定问题。如何判断是否存在一个点X,使得所有点都能在time时间内到达X? 每个人在time时间内的运动范围是以其初始座标为圆心,半径 v*time的一个圆。那么问题就很直观了,只要由time生成的这n个圆存在交,那么所有人就能在time时间内走到一起了。
不过悲剧的是我没有圆面积交的模板。。。所以只能换一种方法求圆的面积交:是否存在一个区域(任意规则),这个区域中有一个点在所有圆内?显然如果存在这样一个点,n个圆就有公共交了。
具体求法:求出所有圆的交点,然后对于一个圆上所有的交点极角排序,如果连续两个交点的中间点能被“看见”,那么着这段弧所在的“区域”也就是“可见”的了。也就意味着当前time可解。
#include<algorithm> #include<iostream> #include<cstring> #include<fstream> #include<sstream> #include<cstdlib> #include<vector> #include<string> #include<cstdio> #include<bitset> #include<queue> #include<stack> #include<cmath> #include<map> #include<set> #define FF(i, a, b) for(int i=a; i<b; i++) #define FD(i, a, b) for(int i=a; i>=b; i--) #define REP(i, n) for(int i=0; i<n; i++) #define CLR(a, b) memset(a, b, sizeof(a)) #define debug puts("**debug**") #define LL long long #define PB push_back #define MP make_pair using namespace std; const double eps = 1e-8; const double PI = acos(-1.0); const double TWO_PI = 2*PI; int dcmp(double x) { if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1; } //将角度标准化 double NormalizeAngle(double rad, double center = PI) { return rad - TWO_PI * floor((rad + PI - center) / TWO_PI); } struct Point { double x, y; Point(double x=0, double y=0):x(x),y(y) { } }; typedef Point Vector; Vector operator + (Vector A, Vector B) { return Vector(A.x+B.x, A.y+B.y); } Vector operator - (Point A, Point B) { return Vector(A.x-B.x, A.y-B.y); } Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); } Vector operator / (Vector A, double p) { return Vector(A.x/p, A.y/p); } bool operator < (const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); } 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 angle(Vector v) { return atan2(v.y, v.x); } double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; } Vector vecunit(Vector x){ return x / Length(x);} //单位向量 Vector Normal(Vector x) { return Point(-x.y, x.x) / Length(x);} //垂直法向量 Vector Rotate(Vector A, double rad) { return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad)); } double Area2(const Point A, const Point B, const Point C) { return Length(Cross(B-A, C-A)); } // 交点相对于圆1的极角保存在rad中 void getCircleCircleIntersection(Point c1, double r1, Point c2, double r2, vector<double>& rad) { double d = Length(c1 - c2); if(dcmp(d) == 0) return; // 不管是内含还是重合,都不相交 if(dcmp(r1 + r2 - d) < 0) return; if(dcmp(fabs(r1-r2) - d) > 0) return; double a = angle(c2 - c1); double da = acos((r1*r1 + d*d - r2*r2) / (2*r1*d)); rad.push_back(NormalizeAngle(a-da)); rad.push_back(NormalizeAngle(a+da)); } const int maxn = 200; int n, T; Point p[maxn]; double s[maxn], R[maxn]; //一个点是否能被所有圆 “看见” inline bool all(Point a) { REP(i, n) if(Length(p[i] - a) > R[i]) return false; return true; } inline bool check(vector<double> sol, Point p, double r) { sort(sol.begin(), sol.end()); REP(i, sol.size()-1) if(sol[i+1] - sol[i] > eps) { //该圆弧的中点 double mid = (sol[i] + sol[i+1]) / 2; //圆弧上的点,往圆内或者圆外走一点 //貌似不加这个会wa。。。 for(int side = -1; side <= 1; side += 2) { double r2 = r - side*eps; Point a = Point(p.x + cos(mid)*r2, p.y + sin(mid)*r2); if(all(a)) return true; } } return false; } inline bool ok(double m) { //构造圆 REP(i, n) R[i] = s[i]*m; REP(i, n) { //枚举每个圆上的所有弧 vector<double> rad; //初始圆的弧是 0~2*PI rad.PB(0); rad.PB(2*PI); REP(j, n) if(i != j) getCircleCircleIntersection(p[i], R[i], p[j], R[j], rad); if(check(rad, p[i], R[i])) return true; } return false; } int main() { scanf("%d", &T); while(T--) { scanf("%d", &n); REP(i, n) scanf("%lf%lf%lf", &p[i].x, &p[i].y, &s[i]); double L=0, R=1e7, M; while(L + eps < R) { M = (L + R) / 2; if(ok(M)) R = M; else L = M; } printf("%.4lf\n", L); } return 0; }