C. Safe Distance(二分 + 并查集)
给定一个X×YX \times YX×Y的矩形,里面有n,(1≤n≤1000)n,(1 \leq n \leq 1000)n,(1≤n≤1000)个点,我们要从点(0,0)(0, 0)(0,0)走到(X,Y)(X, Y)(X,Y),我们要使过程中与这nnn个点的最小距离最大,输出这个最大距离。
考虑二分答案?对于给定的xxx,判断能否从(0,0)(0, 0)(0,0)走到(X,Y)(X, Y)(X,Y),设置l=0,r=min(disS,disT)l = 0, r = min(dis_S, dis_T)l=0,r=min(disS,disT),起点距离其他点的最小值,终点距离其他点的最小值。
可以想象成一个半径为xxx的圆,存在一条路径可以使这个圆穿过去,如果存在三个点,有两对不能使这个圆穿过,则这三个点集构成的集团一定不能使圆穿过。
对于任意两个不同的集团,我们是可以从其中通过的,所以可以考虑用并查集合并,是否存在一条路径是可行的。
如果可行,当且仅当(上边界下边界没有联通)(上边界右边界没有连通)(左边界下边界没有联通)(左边界有边界没有联通),
简单的描述一下,就是从(0,0)(0, 0)(0,0)连向(X,Y)(X, Y)(X,Y)的某条路径没有被隔断。
#include <bits/stdc++.h>using namespace std;const int N = 1e3 + 10;struct Res {double x, y;void read() {scanf("%lf %lf", &x, &y);}
}a[N];int fa[N], n, X, Y; // {n + 1, 上表面} {n + 2, 下表面} {n + 3, 左表面} {n + 4, 右表面}int find(int rt) {return rt == fa[rt] ? rt : fa[rt] = find(fa[rt]);
}double Dis(Res a, Res b) {return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}bool judge(double x) {for (int i = 1; i <= n + 4; i++) {fa[i] = i;}for (int i = 1; i <= n; i++) {if (a[i].y + x > Y) {int u = find(i), v = find(n + 1);fa[u] = v;}if (a[i].y < x) {int u = find(i), v = find(n + 2);fa[u] = v;}if (a[i].x < x) {int u = find(i), v = find(n + 3);fa[u] = v;}if (a[i].x + x > X) {int u = find(i), v = find(n + 4);fa[u] = v;}for (int j = 1; j < i; j++) {if (Dis(a[i], a[j]) < 2 * x) {int u = find(i), v = find(j);fa[u] = v;}}}if ((find(n + 1) == find(n + 2)) || (find(n + 1) == find(n + 4)) || (find(n + 3) == find(n + 2)) || (find(n + 3) == find(n + 4))) {return false;}return true;
} int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);scanf("%d %d %d", &X, &Y, &n);for (int i = 1; i <= n; i++) {a[i].read();}double l = 0, r = max(X, Y);for (int i = 1; i <= 100; i++) {double mid = 0.5 * (l + r);if (judge(mid)) {l = mid;}else {r = mid;}}printf("%.10f\n", l);return 0;
}