codeforces #8D Two Friends (二分答案+计算几何)

题目链接;
点击打开题目链接
题意:
有两个人 Alan Bob ,他们现在都在 A 点,现在Bob想去 B 点,Alan想先到 C 点再去B点。
要求 Alan 走过的长度不能超过最短路长度 +t1 Bob 走过的长度不能超过最短路长度 +t2 ,求两人在一起最多走多久(分开后再汇合不算一起走)?
题解:
Alan 最多走 L1 Bob 最多走 L2 ,注意还要加上 t1 t2 这两个差值。
首先如果 Bob 能陪伴 Alan 全程(即 L2Distance(A,C)+Distance(C,B)) ,那么答案显然为 min(L1,L2) 。此时他们一定是在 Alan 到达 C 之前分开的

否则两人分离时Bob一定还没有经过 C 点 ,这时显然不比一起回家更优。

容易发现答案是单调的,我们不妨二分答案x,即 Alan Bob 走距离为 x 的相同路线后分开。

不妨设分离点为P,当前二分到 mid ,那么:

Distance(P,A)mid

Distance(P,B)L2mid

Distance(P,C)L1Distance(B,C)mid
即:
设分离点为 P ,那么点P必须满足一下三个条件:
P 必须在以A为圆心半径为 x 的圆内,因为他们走的公共距离为x
P 必须在以B为圆心半径为 L2x 的圆内,为了让 Bob 在分开之后能及时返回 B

P必选在以 C 为圆心半径为L1xBC的圆内,因为 Alan 在到达 C 之后还要径直走回B点。

所以如果三个圆相交,那么一定存在这样的点 P

所以容易发现每个不等式中P的范围都是一个圆 。

因此我们只需要判断三个圆是否有公共部分即可 。
判断三个圆是否相交:
三个圆两两相交是必要条件但不是充分条件。
因为可能会有这种情况:

这里写图片描述

在两两相交的前提下,如果有一个小圆内含在一个大圆内的话,那么这三个圆也是相交的。
否则,如果三个圆有公共部分,两两圆必然有 12 个交点。
如图:

这里写图片描述

考虑这三个圆的相交区域,它必然是由若干个圆弧组成的。
所以这块区域的关键点也一定是某两个圆的交点,枚举两两圆的共三组交点,如果有一个交点满足都在三个圆的圆内或圆上那么这三个圆就是相交的

我的是二分了200次。
AC代码:

//#pragma comment(linker, "/STACK:102400000,102400000")
//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <bitset>
#include <iomanip>
#include <list>
#include <complex>
#include <stack>
#include <utility> 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
const double eps = 1e-8;  
const int INF = 1e9+7; 
const ll inf =(1LL<<62) ;
const int MOD = 1e9 + 7;  
const ll mod = (1LL<<32);
const int N =1e6+6; 
const int M=100010;
const int maxn=1001;
#define mst(a) memset(a, 0, sizeof(a))
#define M_P(x,y) make_pair(x,y)
#define pi acos(-1.0)
#define in freopen("in.txt","r",stdin) 
#define rep(i,j,k) for (int i = j; i <= k; i++)  
#define per(i,j,k) for (int i = j; i >= k; i--)  
#define lson x << 1, l, mid  
#define rson x << 1 | 1, mid + 1, r  
const int lowbit(int x) { return x&-x; }
int read(){ int v = 0, f = 1;char c =getchar();
while( c < 48 || 57 < c ){if(c=='-') f = -1;c = getchar();}
while(48 <= c && c <= 57) v = v*10+c-48, c = getchar();
return v*f;}
#define point complex<double>
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); //cinema
  readpoint(house); //house
  readpoint(shop); //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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值