鏈接:CodeForces - 1059D
題意:給出笛卡爾坐標系上 n 個點,求與 x 軸相切且覆蓋了所有給出點的圓的最小半徑。
題解:二分半徑即可。判斷:假設當前二分到的半徑是 R ,因為要和 x 軸相切,所以圓心一定在 y = R 上,對于每一個點而言,圓要覆蓋該點,那么圓心在 y = R 上一定有一段限定區間,所以只要判斷這 n 個區間是否有公共區間即可。卡點:誤差,太可惡了,求區間段時應該將 sqrt(R * R - d * d) 寫成 sqrt(R - d) * sqrt(R + d) ,否則誤差特別大。
#include <bits/stdc++.h> using namespace std;const double EPS = 1e-6; const double INF = 1e17; const int mod = 1e9 + 7; const int maxn = 1e5 + 10; int n; double x[maxn], y[maxn];bool judge(double R) {double l = -INF, r = INF;for(int i = 0; i < n; i++){double d = fabs(y[i] - R);if(d > R) return false;//不可以寫成sqrt(R * R - d * d),這樣誤差會加大double k = sqrt(R - d) * sqrt(R + d);double a = x[i] - k, b = x[i] + k;if(a > r || b < l) return false;l = max(l, a);r = min(r, b);}return true; }bool OK() {bool z = false, f = false;for(int i = 1; i < n; i++){if(y[i] > 0) z = true;else if(y[i] < 0) f = true;}return !(z && f); }int main() {scanf("%d", &n);for(int i = 0; i < n; i++) scanf("%lf%lf", &x[i], &y[i]);if(!OK())return puts("-1") & 0;for(int i = 0; i < n; i++) y[i] = fabs(y[i]);double l = 0, r = INF;for(int i = 0; i < 100; i++){double mid = (l + r) / 2.0;if(judge(mid)) r = mid;else l = mid;}printf("%.6f\n", r);return 0; }