口胡 最小K覆盖圆

这个什么“最小$K$覆盖圆”其实是我瞎整的一个定义。

题目大意

给定平面上$n\le500$个点$(x_i,y_i)$,求出最小的一个半径$r$使得有至少$k$个点被某个以$r$为半径的圆覆盖(包含边界)。

精确到$10^{-8}$。

解析

这是一个悲伤的故事。

如果不那么精确,比如说精确到$10^5$,那什么随机算法爬山啊、退火啊、粒子群啊都能过

这是一个更悲伤的故事

UOJ的clock()好像是假的,这样就造成了薛定谔的TLE

我们考虑二分答案。

显然可以先枚举一个固定点,然后确保这个固定点在圆的边界上。

注意到枚举固定点的时候,包含一个点的圆是一段区间。

那么我们的问题就变成了了给出若干段区间,问有没有一个位子被$k−1$个区间
包含。

暴力二分答案的复杂度是$\mathcal{O}(n^2log\ n\ log\ ans)$的,不太行。

考虑稍微剪一下枝。如果我们当前点对于最优解不合法(搞不定那么多个区间),就直接不管了。

然后random_shuffle一下,复杂度就变成了期望$\mathcal{O}(nlog^2\ nlog\ ans)$了。

Code

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//modify to learn

#define REP(i, s, e) for (int i = s; i <= e; i++)
#define dis(x1, y1, x2, y2) sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>

#define double long double
#define point pair<double, double>
#define x first
#define y second

using namespace std;
const int maxn = 500 + 5;
const double eps = 1e-9, inf = 1e20;
int n, k;
point a[maxn], b[maxn << 1];
double ans;

inline double dist(point A, point B) {return dis(A.x, A.y, B.x, B.y);}
inline int dcmp(double x) {return (x > eps) - (x < -eps);}
bool same(double x, double y) {return fabs(x-y) < eps;}
inline bool cmp(point A, point B) {return A.x < B.x || A.x == B.x && A.y < B.y;}

point operator + (point A, point B) {return make_pair(A.x + B.x, A.y + B.y);}
point operator - (point A, point B) {return make_pair(A.x - B.x, A.y - B.y);}
point operator / (point A, double u) {return make_pair(A.x / u, A.y / u);}
point operator * (point A, double u) {return make_pair(A.x * u, A.y * u);}

inline void calc(point o1, point o2, double r, point& x, point& y)
{
double Dis = dist(o1, o2) / 2;
point mid = (o1 + o2) / 2, p = (o2 - o1) / 2;
p = make_pair(p.y, -p.x) / Dis * sqrt(r * r - Dis * Dis);
x = mid + p, y = mid - p;
}

inline bool check(point o, double r)
{
int m = k - 1, p = 0;
REP(i, 1, n)
if (dcmp(dist(o, a[i])) && dcmp(dist(o, a[i]) - 2 * r) <= 0)
{
point x,y;
calc(o, a[i], r, x, y);
double ax = atan2(x.y - o.y, x.x - o.x), ay = atan2(y.y - o.y, y.x - o.x);
b[++p] = make_pair(ax, -1);
b[++p] = make_pair(ay, 1);
if (ax > ay) m--;
}
if (m <= 0) return 1;
sort(b + 1, b + p + 1, cmp);
REP(i, 1, p)
if ((m += b[i].y) <= 0) return 1;
return 0;
}


int main()
{
#ifndef ONLINE_JUDGE
freopen("A.in", "r", stdin);
freopen("A.out", "w", stdout);
#endif
srand(233);
cin >> n >> k;
REP(i, 1, n) scanf("%LF%LF", &a[i].x, &a[i].y);
random_shuffle(a + 1, a + n + 1);
double L, R = inf;
REP(i, 1, n)
if (check(a[i], R))
{
L = 0;
while (R - L > eps)
{
double MID = (L + R) / 2;
if (check(a[i], MID)) R = MID;
else L = MID;
}
}
printf("%.8LF\n", R);
return 0;
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×