呜呜,我旋转卡壳做的,我先构造凸包(我的凸包没有构造错,我通过了 模板),但是我 WA 了。
构造完凸包,然后我旋转卡壳 (脑子卡壳) ,我遍历凸包中所有的边,设目前遍历到的边是凸包中的第 edge 和第 edge + 1 个元素,找到离这条边最远的点 pos,如果最终拟合的直线和现在的 edge 与 edge + 1 平行,那么 res = len(pos) / 2.0 ,代码中有我的思路:
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
const double inf = 1e9;
int n;
struct node { double x, y; } a[N];
int ttu[N], cnt; /// 凸包
double kkk(int i, int j) /// a[i] 和 a[j] 连线的斜率,i -> j
{
if (a[i].x == a[j].x) return (a[j].y - a[i].y > 0) ? inf : -inf; /// 斜率相同,但要考虑正负
return (a[j].y - a[i].y) / (a[j].x - a[i].x);
}
double dis(int i, int j)
{
double xx = a[i].x - a[j].x, yy = a[i].y - a[j].y;
return sqrt(xx * xx + yy * yy);
}
struct stack
{
int data[N], size;
void push(int a) { data[++size] = a; }
void pop() { size--; }
int top1() { return data[size]; }
int top2() { return data[size - 1]; }
};
void solve(bool flag) /// flag = true 代表下凸包,false 代表上凸包
{
stack s; s.size = 0; s.push(1);
for (int i = 2; i <= n; i++)
{
while (s.size >= 2)
{
int t1 = s.top2(), t2 = s.top1();
if (kkk(t1, i) == kkk(t1, t2) && dis(t1, i) > dis(t1, t2)) { s.pop(); continue; }
/// 看 t1 -> a[i] 能否替换 t1 -> t2,斜率越小越好 | 斜率相同但距离 i 更大
if (flag) { if (kkk(t1, i) < kkk(t1, t2)) { s.pop(); } else break; }
if (!flag) { if (kkk(t1, i) > kkk(t1, t2)) { s.pop(); } else break; }
}
s.push(i);
}
if (flag) { for (int i = 1; i <= s.size; i++) ttu[++cnt] = s.data[i]; }
else { for (int i = s.size - 1; i >= 2; i--) ttu[++cnt] = s.data[i]; } /// 不算开头结尾
}
double res;
int edge; /// edge 和 edge + 1 的连线,res = max{ 与之距离最远的点的距离 / 2 }
int pos; /// 与之最远的点的距离的点是 pos
/// edge 和 pos 表示 ttu[edge] 和 ttu[pos],换算成 a[ttu[edge]] 和 a[ttu[pos]]
double len(int id) /// id 与直线 edge,edge + 1 的距离
{
/// 点 P(x0, y0),y = kx + b,len = |kx0 - y0 + b| / sqrt(l + k ^ 2)
double k = (a[ttu[edge + 1]].y - a[ttu[edge]].y) / (a[ttu[edge + 1]].x - a[ttu[edge]].x);
/// k * a[ttu[edge]].x + b = a[ttu[edge]].y
double b = a[ttu[edge]].y - k * a[ttu[edge]].x;
double ret = (k * a[ttu[id]].x - a[ttu[id]].y + b) / sqrt(1 + k * k);
return (ret < 0) ? -ret : ret;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lf%lf", &a[i].x, &a[i].y);
/// 不按 y 排序可能 WA
sort(a + 1, a + n + 1, [](node i, node j) { return (i.x == j.x) ? i.y < j.y : i.x < j.x; });
solve(true); solve(false);
/// 对于每条边,找最远的点 pos 使得 len(pos) 在所有点中是《最大》的
/// res = 所有 len(pos) 的《最小》值
edge = 1; pos = 1; res = -1;
for (int i = 1; i <= n; i++) { double tmp = len(i); if (tmp > res) { res = tmp; pos = i; } }
edge = 2;
while (edge != 1)
{
/// 比较是 pos 好还是 pos + 1 (- cnt) 好
int new_pos = pos + 1; if (new_pos > cnt) new_pos -= cnt;
while (true)
{
int _new = len(new_pos), last = len(pos);
if (_new > last) { pos = new_pos; new_pos++; if (new_pos > cnt) new_pos -= cnt; }
else break;
}
int _this = len(pos); if (_this < res) res = _this;
edge++; if (edge > cnt) edge -= cnt;
}
res /= 2.0; /// 半径
printf("%.2lf", res);
return 0;
}