ACM - CF1059D 二分/三分
题意:
n个点,给定(Xi,Yi)的坐标,问:找到一个最小半径的圆,既与x轴相切,又包含所有的n个点。
边界条件:
当n个点,既有出现在x轴上方的,又有出现在x轴下方的点时,不存在。
当n个点,出现了至少两个点在x轴上时,不存在。
画画图就能想明白。
圆需要与x轴相切,必然与x轴只有1个交点。
二分做法:
给定r,判断是否可行。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 20;
int n;
double x[maxn];
double y[maxn];
int ok(double r){
double xleft = -1e15, xright = 1e15, tmp;
for(int i = 1; i <= n; i++){
if (y[i] > 2 * r) return 0;
tmp = sqrt(2 * r * y[i] - y[i] * y[i]);
xleft = max(xleft, x[i] - tmp);
xright = min(xright, x[i] + tmp);
}
return xleft <= xright;
}
int main(){
int xcnt = 0;
int yup = 0;
int ydown = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%lf%lf", &x[i], &y[i]);
if (y[i] == 0) xcnt ++;
if (y[i] > 0) yup = 1;
if (y[i] < 0) ydown = 1, y[i] = - y[i];
}
if ((yup && ydown) || (xcnt >= 2))
printf("-1\n");
//if (yup && ydown) printf("-1\n");
else{
int cnt = 100;
double l = 0, r = 1e15, mid, ans;
while(cnt --){
mid = (l + r) / 2.0;
if (ok(mid)){
ans = mid;
r = mid;
}
else l = mid;
}
printf("%.6lf\n", ans);
}
return 0;
}
着重想讲一讲三分
二分:处理的是单调性问题。能够找到边界值。最大,最小。在[L,R]区间内,函数是单调的。有些值,ok;有些值不ok。
三分:可以处理的是带有一个拐点的问题。能够找到极值。极大或极小。在[L,R]区间内,函数有极值。在极值两侧,单调性相反。
本题中,假设圆心坐标为(x,r),即有:
(x - xi) ^ 2 + (r - yi) ^ 2 = r ^ 2
得到:r = ((x - xi) ^ 2 + yi ^ 2) / (2 * yi)
那么有R = max(r1, r2, ..., rn)
考虑x往-inf,和inf两侧,即x正负半轴走,R都是变大的。
即存在一个极值X0,有左边递减,右边递增,故可以三分。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 20;
int n;
double x[maxn];
double y[maxn];
double calc(double xx){
double r = 0;
for(int i = 1; i <= n; i++)
r = max(r, ((xx - x[i]) * (xx - x[i]) + y[i] * y[i]) / (2.0 * y[i]));
return r;
}
int main(){
int xcnt = 0;
int yup = 0;
int ydown = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%lf%lf", &x[i], &y[i]);
if (y[i] == 0) xcnt ++;
if (y[i] > 0) yup = 1;
if (y[i] < 0) ydown = 1, y[i] = - y[i];
}
if ((yup && ydown) || (xcnt >= 2))
printf("-1\n");
//if (yup && ydown) printf("-1\n");
else{
int cnt = 100;
double l = -1e7, r = 1e7, dx, ml, mr;
while(cnt --){
dx = (r - l) / 3.0;
ml = l + dx;
mr = r - dx;
if (calc(ml) < calc(mr)) r = mr;
else l = ml;
}
printf("%.6lf\n", calc(r));
}
return 0;
}
查看6道真题和解析