【描述】
球球从来都不承认自己很圆,它只认为自己很胖。
球球为了证明这个世界上有东西比它更圆,所以它准备用圆规在一个二维平面上画若干个圆。现在球球 已经钦定了 n 个可以作为圆心的坐标,它会选择若干个点,并且将它们按照一定顺序连接后能够形成一 个凸多边形, 然后以这些点为圆心来画圆,同时使得这些圆相交部分的面积恰好是 0。
就在球球准备动手证明这个世界上有东西比它更圆的时候,它忽然发 现它的圆规是一次性的:一旦指定 了半径之后就无法再更改了,只能用来画半径固定的圆了。
现在球球很苦恼,但是为了证明这一切,它仍然想完成它的杰作。球球认为半径越大,圆就显得更加的 圆滚滚,因此它就想知道它能够画的最大的圆的半径是多少。
这个问题对于曾经拿过全国 rk1 的球球来说太 Simple,但是它正在努力将圆规升级,让圆规可以反复使 用。这样一来它就没有时间直接调用它自 己写的、能够得到任何问题答案的 Ball.GetAns() 函数得到答 案,所以需要你来告诉它结果了。
【输入】
第一行一个整数 n,含义见题面。(n<=4000)
接下来是 n 行,每行两个整数 x, y,代表球球钦定的坐标。
【输出】
一行一个实数,表示球球可以选择的最大半径。 答案误差在10−6以内都是可以被接受的。 时限(2s)可能比较紧,请注意常数优化。
【样例输入】
3
0 0
1 1
1 0
【样例输出】
0.50000000000
思路
首先,一个显然的结论,三角形一定是最优的。因为对于任意一个多边形,把它变成三角形一定不会使答案更差。
第一种做法是,枚举每一个点,然后将其它点相对于这个点的极角进行排序。对于每一个枚举的点,我们只考虑夹这个点的两条边是否为三角形中三边中最小值。换句话说,我们需要保证对边不是最小值。这里就需要用到一个简单的结论,任意三角形满足“大角对大边”。因此,如果我们保证更新答案时两条边的夹角大于六十度,那么对边就一定不是最小值。然后,对于剩下的两条边,我们可以利用单调队列维护最值。因此总时间复杂度为O(
n
2
l
o
g
n
n^{2}log {n}
n2logn)。但是听说这道题卡常卡到标程超时。
这道题做法很多,包括二分+旋转卡壳。其中有一种做法代码十分简单。考虑对于
n
2
2
\frac{n^{2}}{2}
2n2条边按大小进行排序。按边权从大到小依次把每条边加进来。显然,构成三角形时加入的那条边就是答案。问题在于怎么判断是否构成三角形。考虑加入边的两个顶点,如果它们已经同时与某个点直接相连,那么就会构成三角形。所以我们可以用bitset维护每个点和哪些点已经有边,判断时将bitset交起来判断是否有1即可。这种做法属于玄学复杂度,瓶颈在于会枚举多少条边才能得到最优解。最坏情况O(
n
3
32
\frac{n^{3}}{32}
32n3),实际上远小于这个复杂度。
//标算做法
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 10010
#define Sqr(x) ((x)*(x))
#define mp make_pair
#define fr first
#define sd second
const double Pi=acos(-1);
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int x[MAX],y[MAX],n,ans;
pair<double,int> a[MAX<<1];
int Q[MAX],L,R;
void add(int x){while(L<=R&&Q[R]<x)--R;Q[++R]=x;}
void del(int x){if(L<=R&&Q[L]==x)++L;}
int main()
{
n=read();
for(int i=1;i<=n;++i)x[i]=read(),y[i]=read();
for(int i=1;i<=n;++i)
{
int k=0;L=1;R=0;
for(int j=1;j<=n;++j)
if(i^j)a[++k]=mp(atan2(y[j]-y[i],x[j]-x[i]),Sqr(x[j]-x[i])+Sqr(y[j]-y[i]));
sort(&a[1],&a[k+1]);
for(int j=1;j<=k;++j)a[j+k]=a[j],a[j+k].fr+=2*Pi;
for(int j=1,l=1,r=1;j<=k;++j)
{
while(a[r].fr-a[j].fr<=Pi)add(a[r++].sd);
while(a[l].fr-a[j].fr<=Pi/3)del(a[l++].sd);
if(L<=R)ans=max(ans,min(a[j].sd,Q[L]));
}
}
printf("%.10lf\n",sqrt(ans)/2);
return 0;
}
//玄学做法
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=4e3+1;
inline int red()
{
int data=0;int w=1; char ch=0;
ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
int n,m,u,v;
int x[N],y[N];
bitset<N>b[N];
int cnt=0;
struct node{
int len;
int u,v;
}a[N*N>>1];
inline bool cmp(node a,node b){return a.len>b.len; }
inline int dis(int i,int j){return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);}
int main()
{
n=red();
for(int re i=1;i<=n;i++)
{
x[i]=red(),y[i]=red();
for(int re j=1;j<i;j++)
a[++cnt]=(node){dis(i,j),i,j};
}
sort(a+1,a+cnt+1,cmp);
for(int re i=1;i<=cnt;i++)
{
u=a[i].u,v=a[i].v;
if((b[u]&b[v]).any())
{
printf("%.10lf",sqrt(a[i].len)/2);
return 0;
}
b[u][v]=1,b[v][u]=1;
}
}