1. 程式人生 > >codeforces #8D Two Friends (二分答案+計算幾何)

codeforces #8D Two Friends (二分答案+計算幾何)

題目連結;

題意:

有兩個人AlanBob,他們現在都在A點,現在Bob想去B點,Alan想先到C點再去B點。

要求Alan走過的長度不能超過最短路長度+t1Bob走過的長度不能超過最短路長度+t2,求兩人在一起最多走多久(分開後再匯合不算一起走)?

題解:

Alan最多走L1Bob最多走L2,注意還要加上t1t2這兩個差值。

首先如果Bob能陪伴Alan全程(即L2Distance(A,C)+Distance(C,B)),那麼答案顯然為min(L1,L2) 。此時他們一定是在Alan到達C之前分開的

否則兩人分離時Bob一定還沒有經過C
點 ,這時顯然不比一起回家更優。

容易發現答案是單調的,我們不妨二分答案x,即AlanBob走距離為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為圓心半徑為L1xB
C
的圓內,因為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;
}