题意
在坐标轴上给出n
条线段,圆心在x
轴的[0,L]
上移动,在该圆不和任何线段相交的情况下最大的半径是多少?
题解
模拟退火,因为模拟退火可以并行执行,所以先随机出多个点的位置(多组可能解),然后对这些解进行模拟退火,得出点到线段的最短距离用的是失量法。
点与线段一共会有下列两种位置。
- P在AB上,那么P离AB的最短距离便是 A × B / l e n A B {A×B}/{len_{AB}} A×B/lenAB,这里的乘是向量叉乘(△ABP的面积)。
- P不在AB上,那么AB·AP 和 AB·BP 肯定是一个锐角(>0)一个钝角(<0),所以这时候只要取 max(PA,PB)即可。这里的·是向量点乘。
出处:刘汝佳训练指南几何专题
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 5;
const int rndcnt = 10;
const double eps = 1e-5;
struct Point{
double x, y;
};
struct Line{
Point s, e;
};
Line ll[maxn];
double p[rndcnt];
double dot(Point x, Point y) {
return x.x * y.x + x.y * y.y;
}
double det(Point a, Point b) {
return a.x * b.y - a.y * b.x;
}
double dist(Point a, Point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
Point sub(Point a, Point b) {
return Point{a.x - b.x, a.y - b.y};
}
// 这是WHU大佬的点到线段最短距离的模板我也看不懂
double point2seg(double x, const Line &l) {
Point P{x, 0};
Point v = sub(l.e, l.s);
Point w = sub(P, l.s);
double c1, c2;
if ((c1 = dot(w, v)) < eps) {
return dist(P, l.s);
}
if ((c2 = dot(v, v)) < c1 + eps) {
return dist(P, l.e);
}
double b = c1 / c2;
return dist(P, Point{l.s.x + b * v.x, l.s.y + b * v.y});
}
void sol() {
int n, L;
double ans = -1e100;
double step = 100;
scanf("%d%d", &n, &L);
step = L;
for (int i = 0; i < n; i++) {
scanf("%lf%lf%lf%lf", &ll[i].s.x, &ll[i].s.y, &ll[i].e.x, &ll[i].e.y);
}
for (int i = 0; i < rndcnt; i++) {
p[i] = 1. * rand() / RAND_MAX * L; // 随机10组解
}
double rans = 0;
while (step > eps) {
for (int i = 0; i < rndcnt; i++) {
double tmin = -1e100;
double rmin = p[i];
for (int j = 0; j < rndcnt; j++) {
double cmin = 1e100;
double nxt = p[i] + step * (1. * rand() / RAND_MAX - 0.5);
if (nxt < 0 || nxt > L) continue;
for (int k = 0; k < n; k++) {
double tans = point2seg(nxt, ll[k]);
cmin = min(cmin, tans);
}
if (cmin > tmin) {
tmin = cmin;
rmin = nxt;
}
}
p[i] = rmin;
if (tmin > ans) {
rans = rmin;
ans = tmin;
}
}
step = step * 0.95;
}
cerr << rans << endl;
printf("%.3f\n", ans);
}
int main() {
int t;
srand(time(NULL));
scanf("%d", &t);
while (t--) sol();
return 0;
}