题目描述
题解
最小值最大,二分答案(实数)
判定?
二分出答案mid之后相当于是有一些点不能同时选(距离
<
<script type="math/tex" id="MathJax-Element-1"><</script>mid)
同时每一组两个点必须选一个
就是一个经典的2-SAT问题
我们只需要判断是否有解即可
假设
A1,B2
不能同时选,那么如果选
A1
的话,
B1
是必选的
同样地,如果选
B2
,那么
A2
必选
那么连边
A1
->
B1
,
B2
->
A2
用tarjan求强连通分量
如果某一组的两个点在同一个强连通分量里,则无解
因为p能到达的点是选p则必选的点
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 505
const double eps=1e-9;
int dcmp(double x)
{
if (x<=eps&&x>=-eps) return 0;
return (x>0)?1:-1;
}
struct Point
{
double x,y;
Point (double X=0,double Y=0)
{
x=X,y=Y;
}
};
int n,cnt,dfs_clock,top,scc;
double x,y,ans;
Point p[N*2];
int tot,point[N*2],nxt[N*N*10],v[N*N*10];
int dfn[N*2],low[N*2],vis[N*2],stack[N*2],belong[N*2];
double Dis(Point P,Point Q)
{
double x=P.x-Q.x,y=P.y-Q.y;
return sqrt(x*x+y*y);
}
void add(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void tarjan(int x)
{
dfn[x]=low[x]=++dfs_clock;stack[++top]=x;vis[x]=1;
for (int i=point[x];i;i=nxt[i])
if (!dfn[v[i]])
{
tarjan(v[i]);
low[x]=min(low[x],low[v[i]]);
}
else if (vis[v[i]])
low[x]=min(low[x],dfn[v[i]]);
if (dfn[x]==low[x])
{
int now=0;++scc;
while (now!=x)
{
now=stack[top--];
belong[now]=scc;
vis[now]=0;
}
}
}
bool check(double mid)
{
tot=0;memset(point,0,sizeof(point));
for (int i=0;i<=cnt;++i)
for (int j=i+1;j<=cnt;++j)
if (dcmp(Dis(p[i],p[j])-mid)<0)
{
add(i+1,(j^1)+1);
add(j+1,(i^1)+1);
}
memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
memset(belong,0,sizeof(belong));dfs_clock=top=scc=0;
for (int i=1;i<=cnt+1;++i)
if (!dfn[i]) tarjan(i);
for (int i=0;i<=cnt;i+=2)
if (belong[i+1]==belong[(i^1)+1])
return false;
return true;
}
double find()
{
double l=0.0,r=20000.0,mid,ans=0.0;
while (dcmp(r-l)!=0)
{
mid=(l+r)/2;
if (check(mid)) ans=l=mid;
else r=mid;
}
return ans;
}
int main()
{
while (~scanf("%d",&n))
{
cnt=-1;
for (int i=1;i<=n;++i)
{
scanf("%lf%lf",&x,&y);
p[++cnt]=Point(x,y);
scanf("%lf%lf",&x,&y);
p[++cnt]=Point(x,y);
}
ans=find();
printf("%.2lf\n",ans/2);
}
}