题意是有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;
}
博客介绍了如何解决一个问题,即找到一个点X,使得所有具有不同坐标和速度的人到达该点所需时间最长的人的时间最短。通过将问题转化为圆的面积交,使用二分法来枚举时间,并通过判断是否存在一个点位于所有半径为速度乘以时间的圆内来确定解决方案。由于没有现成的圆面积交模板,采用了求圆交点并进行极角排序的方法,找出所有圆内可见的区域来确定可行的时间。
824

被折叠的 条评论
为什么被折叠?



