SPOJ AMR12C Entmoot(二分+圆的面积交)

题意是有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;
}


你可能感兴趣的:(SPOJ AMR12C Entmoot(二分+圆的面积交))