codeforces 8d 计算几何

题意:甲乙两人看完电影准备回家,乙想直接回家,甲想先去超市再回家,已知甲可以多走t1的“冤枉路”,乙可以多走t2的“冤枉路”,问甲乙最多同行多远后分离

题解:设甲最多走L1,乙最多走L2,
首先如果乙b能陪伴甲全程(即L2≥Distance(A,C)+Distance(C,B))L2≥Distance(A,C)+Distance(C,B)),那么答案显然为min(L1,L2) 。

如若不然,显然答案是单调的,可以二分查找

不妨设分离点为P,当前二分到mid,那么:
Distance(P,A)≤mid
Distance(P,B)≤L2−mid
Distance(P,C)≤L1−Distance(B,C)−mid
即:
设分离点为P,那么点P必须满足以下三个条件:
P必须在以A为圆心半径为x的圆内,因为他们走的公共距离为x
P必须在以B为圆心半径为L2−x的圆内,为了让乙在分开之后能及时返回B点
P必选在以C为圆心半径为L1−x−BC的圆内,因为甲在到达C之后还要径直走回B点。

于是问题就转换成判断三个圆相交的问题,就可以套模板了

#include <bits/stdc++.h>
#define point complex<double>
using namespace std;

const double eps = 1e-8;  
double t1, t2;
point cinema, shop, house;

void readpoint(point &p){
  double x, y;
  scanf("%lf %lf", &x, &y);
  p = point(x, y);
}

bool inter(point a, double r_a, point b, double r_b, point c, double r_c){ //以c为主圆求a b焦点判相交 
  if (abs(c - a) <= r_a && abs(c - b) <= r_b) return true;
  b -= a; 
  c -= a; //以a为原点 
  point r = point(b.real() / abs(b), b.imag() / abs(b)); //将x轴正方向置为b
  b /= r; 
  c /= r;
  double d = (r_a * r_a - r_b * r_b + abs(b) * abs(b)) / (2 * abs(b));
  double h = sqrt(max(r_a * r_a - d * d, 0.0));
  if (abs(h * h + (d - abs(b)) * (d - abs(b))) - r_b * r_b > eps) return false;
  if (abs(point(d, h) - c) <= r_c || abs(point(d, -h) - c) <= r_c) return true;
  return false;
}

bool check(point a, double r_a, point b, double r_b, point c, double r_c){ //判断三圆是否相交 
  if (r_a <= eps || r_b <= eps || r_c <= eps) return false; //有空集 
  r_a = max(r_a, 0.0); 
  r_b = max(r_b, 0.0); 
  r_c = max(r_c, 0.0);
  if (inter(a, r_a, c, r_c, b, r_b)) return true;
  if (inter(b, r_b, a, r_a, c, r_c)) return true;
  if (inter(c, r_c, b, r_b, a, r_a)) return true;
  return false;
}

int main(){
  scanf("%lf %lf", &t1, &t2);
  readpoint(cinema);
  readpoint(house);
  readpoint(shop);

  if (abs(shop - cinema) + abs(house - shop) <= abs(cinema - house) + t2){	//Alan <= Bob + t2
    printf("%lf\n", min(abs(cinema - house) + t2, abs(shop - cinema) + abs(house - shop) + t1));   
  } 
  else{
    double l, r, mid;
    l = 0; 
    r = min(abs(cinema - house) + t2, abs(shop - cinema) + abs(house - shop) + t1);

    for(int i = 1; i <= 200; i ++){ 
      mid = (r + l) / 2;
      if (check(cinema, mid, shop, abs(shop - cinema) + t1 - mid, house, abs(house - cinema) + t2 - mid)){
        l = mid;
      } 
      else {
        r = mid;
      }
    }
    printf("%.4lf\n", l);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38759433/article/details/86535393