https://ac.nowcoder.com/acm/contest/120454/F
属于典型的“是否存在一个 X ∈ R 可以满足所有约束”的问题
浮点二分
二分解法: 二分答案$r^2$,然后将区间$x-\sqrt{r^2-y^2}$到$x+\sqrt{r^2-y^2}$ 区间覆盖判断,若所有区间存在交集,则返回true
先看错解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include<bits/stdc++.h> using namespace std; struct node { double x, y; }; void solve(){ int n; cin >> n; vector<node> a(n + 1); for (int i = 1; i <= n; ++i) { cin >> a[i].x >> a[i].y; } auto check = [&](double R2) -> bool { double L = -1e9, R = 1e9; for (int i = 1; i <= n; ++i) { double X = R2 - a[i].y * a[i].y; double l = a[i].x - sqrt(X); double r = a[i].x + sqrt(X); L = max(L, l); R = min(R, r); } return L <= R; }; double l = -1e9, r = 1e9; double ans; while (l <= r) { double mid = l + (r - l) / 2.0; if (check (mid)) { ans = mid; l = mid + 1; } else { r = mid - 1; } } cout << sqrt(ans) << endl; } int main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int _=1; while(_--) solve(); return 0; }
|
错误点:
在二分一个 double,却使用了整数二分写法
1 2 3 4 5
| while (l <= r) { mid = l + (r - l) / 2; if (check(mid)) l = mid + 1; else r = mid - 1; }
|
使用这种比较完全不收敛,
而且 mid + 1 会跳过正确答案,导致永远不精确。
check(R2) 没有边界判断
如果 $r^2$ < $y^2$,$x^2$为负,会 sqrt(负数) → nan → WA/RE
二分区间不应该是 [-1e9, 1e9]
[-1e9, 1e9] 有一半是无效区间
因为我们二分答案 $r^2$ ,而半径平方不能为负
正确写法:浮点二分(模板)
1 2 3 4 5 6 7
| double l = 0, r = 2e8; for (int i = 0; i < 60; i++) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; }
|
不能用 while(l <= r)!!
最终无 bug 版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> using namespace std; struct node { double x, y; }; void solve(){ int n; cin >> n; vector<node> a(n + 1); for (int i = 1; i <= n; ++i) { cin >> a[i].x >> a[i].y; } auto check = [&](double R2) -> bool { double L = -1e9, R = 1e9; for (int i = 1; i <= n; ++i) { double X = R2 - a[i].y * a[i].y;
if (X < 0) return 0; double l = a[i].x - sqrt(X); double r = a[i].x + sqrt(X); L = max(L, l); R = min(R, r); } return L <= R; }; double l = 0, r = 1e9; for (int i = 1; i <= 60; ++i) { double mid = l + (r - l) / 2; if (check(mid)) { r = mid; } else { l = mid; } } cout << setprecision(6) << sqrt(r) << endl; } int main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int _=1; while(_--) solve(); return 0; }
|
三分
三分法是一种用于在“单峰/单谷函数f(x)”上寻找最优值(最大或最小)的算法。
三分法思想:
把区间 [l, r] 分成三段: l —— m1 —— m2 —— r
其中: m1 = l + (r - l) / 3 (左三等分点)
m2 = r - (r - l) / 3(右三等分点)
然后比较: f(m1) 和 f(m2)
如果 f(m1) < f(m2)
→ 最高点在区间 (m1, r)
→ 扔掉左边部分 l = m1
如果 f(m1) > f(m2)
→ 最高点在区间 (l, m2)
→ 扔掉右边部分 r = m2
三分法代码模板(整数区间)
1 2 3 4 5 6 7 8 9 10 11 12
| int ternary(int l, int r) { while (r - l >= 3) { int m1 = l + (r - l) / 3; int m2 = r - (r - l) / 3;
if (f(m1) < f(m2)) l = m1; else r = m2; } }
|
三分法代码模板(实数区间)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| double ternary(double l, double r) { const double eps = 1e-12;
while (r - l > eps) { double m1 = l + (r - l) / 3; double m2 = r - (r - l) / 3;
if (f(m1) < f(m2)) { l = m1; } else { r = m2; } } }
|
算了,直接看题吧
我们要在 x 轴上选一个点 X,使得到所有点 ($x_i$, $y_i$) 的 最大距离最小:
$f(x) = \max\limits_{1 \le i \le n}\sqrt{(X - x_i)^2 + y_i^2}$
自己感受一下:$X$从x轴左边到x轴右边,$\max\limits_{1 \le i \le n}\sqrt{(X - x_i)^2 + y_i^2}$ 肯定先变小后变大
所以这个函数 是凹的(单峰),我们要搜$f(x)$ 的最小值(对应$x$为X),因此可用 三分法 搜索 X。
代码(AC 版本,C++)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include<bits/stdc++.h> using namespace std;
struct node { int x, y; };
void solve(){ int n; cin >> n; vector<node> a(n + 1); for (int i = 1; i <= n; ++i) { cin >> a[i].x >> a[i].y; }
auto f = [&](double x) -> double { double mx = 0; for (int i = 1; i <= n; ++i) { double dx = a[i].x - x, dy = a[i].y; mx = max(mx, sqrt(dx * dx + dy * dy)); } return mx; };
double l = -10000, r = 10000; for (int i = 1; i <= 60; ++i) { double m1 = l + (r - l) / 3; double m2 = r - (r - l) / 3; if (f(m1) > f(m2)) { l = m1; } else { r = m2; } }
cout << setprecision(6) << f((l + r) / 2.0) << endl; } int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int _=1; while(_--) solve(); return 0; }
|
收工!