P1742 最小圓覆蓋
阿新 • • 發佈:2018-12-27
\(\color{#0066ff}{題目描述}\)
給出N個點,讓你畫一個最小的包含所有點的圓。
\(\color{#0066ff}{輸入格式}\)
先給出點的個數N,2<=N<=100000,再給出座標Xi,Yi.(-10000.0<=xi,yi<=10000.0)
\(\color{#0066ff}{輸出格式}\)
輸出圓的半徑,及圓心的座標,保留10位小數
\(\color{#0066ff}{輸入樣例}\)
6
8.0 9.0
4.0 7.5
1.0 2.0
5.1 8.7
9.0 2.0
4.5 1.0
\(\color{#0066ff}{輸出樣例}\)
5.0000000000
5.0000000000 5.0000000000
\(\color{#0066ff}{資料範圍與提示}\)
none
\(\color{#0066ff}{題解}\)
隨機增量法
前置知識,三點定圓
設圓上有三個點\(A(x_1, y_1), B(x_2, y_2), C(x_3, y_3)\)
我們可以找這些點的連線的任兩條線,找它們垂直平分線的交點就是圓心
易得向量(線段)AB,BC
設兩條線段的垂直平分線為\(A_1x+B_1y=C_1,A_2x+B_2y=C_2\)
上式的A和B分別是兩個向量的x和y(法向量(可以帶入驗證))
之後帶入AB,BC的中點來求出兩個C
聯立兩個垂直平分線,解出來
\(x=\frac{C_2*B_1-C_1*B_2}{A_2*B_1-A_1*B_2}, y=\frac{C_2*A_1-C_1*A_2}{B_2*A_1-B_1*A_2}\)
有了圓心,半徑自然好求,隨便找圓上一點(A,B,C)與圓心求距離就是半徑了
隨機增量
初始設定第一個點為圓心,半徑為0
依次掃每個點,如果不在當前圓內,則以那個點為圓心
再次掃從頭開始到當前的每個點,如果不在當前圓中
則當前點與那個點作為新圓直徑,構成當前圓,再次掃到當前每個點
如果仍有不在圓內的,三點定圓
#include <bits/stdc++.h> #define _ 0 #define LL long long inline LL in() { LL x = 0, f = 1; char ch; while(!isdigit(ch = getchar()))(ch == '-') && (f = -f); while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar(); return x * f; } const int maxn = 1e5 + 10; const double eps = 1e-14; struct node { double x, y; node(double x = 0, double y = 0) :x(x), y(y) {} double mod() { return sqrt(x * x + y * y); } friend node operator - (const node &a, const node &b) { return node(a.x - b.x, a.y - b.y); } friend node operator + (const node &a, const node &b) { return node((a.x + b.x) / 2.0, (a.y + b.y) / 2.0); } }e[maxn]; int n; void circle(node a, node b, node c, node &o, double &r) { node ab = b - a, bc = c - b, mid1 = a + b, mid2 = b + c; double A1 = ab.x, B1 = ab.y, A2 = bc.x, B2 = bc.y; double C1 = A1 * mid1.x + B1 * mid1.y, C2 = A2 * mid2.x + B2 * mid2.y; o = node((C2 * B1 - C1 * B2) / (A2 * B1 - A1 * B2), (C2 * A1 - C1 * A2) / (B2 * A1 - B1 * A2));r = (o - a).mod(); } void work() { node o = e[1]; double r = 0; for(int i = 2; i <= n; i++) if((e[i] - o).mod() - r > eps) { o = e[i], r = 0; for(int j = 1; j <= i - 1; j++) if((e[j] - o).mod() - r > eps) { o = e[i] + e[j]; r = (o - e[i]).mod(); for(int k = 1; k <= j - 1; k++) if((e[k] - o).mod() - r > eps) circle(e[i], e[j], e[k], o, r); } } printf("%.10f\n%.10f %.10f", r, o.x, o.y); } int main() { n = in(); for(int i = 1; i <= n; i++) scanf("%lf%lf", &e[i].x, &e[i].y); work(); return 0; }