题意:现有n组气球(气球可以看成球体),每组有一个红气球一个蓝气球,已知每组气球的球心。要求从这n组气球中每组挑出一个气球放入花园中,不允许任意两球之间有重叠,求满足条件的气球的最大半径。
解法:因为每组就两个球,很容易想到二分+2 sat。
首先二分枚举半径R。然后建图,若球x与球y的球心距小于R,则x->y',y->x'连边。跑一遍2 sat,并判断一组中的两个点是否在同一连通分量中。
题目有坑,因为“The results should be rounded to three decimal places. You should promise that there is still no overlap for any two balloons after rounded.”所以四舍五入后以后还要判断一下是否会发生重叠。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define maxn 505 #define maxm 1000005 #define EPS 1e-4 int n; struct node { int v; int next; }e[maxm]; int ecnt, pre[maxn]; int dfn[maxn], low[maxn], bel[maxn], stack[maxn]; bool instack[maxn]; int top, cnt, Dindex; struct Node { int x,y,z; }p[maxn]; double dis[maxn][maxn]; void addEdge(int u,int v) { e[ecnt].v=v; e[ecnt].next=pre[u]; pre[u]=ecnt++; } double cal(int a,int b) { return sqrt(1.0*(p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)+(p[a].z-p[b].z)*(p[a].z-p[b].z)); } void build(double key) { ecnt=0; memset(pre,-1,sizeof(pre)); for (int i=1;i<=2*n;i++) for (int j=1;j<=2*n;j++) if (i!=j&&i+n!=j&&i!=j+n&&dis[i][j]+EPS<key) { if (j<=n) addEdge(i,j+n); else addEdge(i,j-n); if (i<=n) addEdge(j,i+n); else addEdge(j,i-n); } } void tarjan(int u) { dfn[u]=low[u]=++Dindex; stack[++top]=u; instack[u]=1; for (int i=pre[u];i!=-1;i=e[i].next) { int v=e[i].v; if (!dfn[v]) { tarjan(v); if (low[v]<low[u]) low[u]=low[v]; } else if (instack[v]&&dfn[v]<low[u]) low[u]=dfn[v]; } int v; if (dfn[u]==low[u]) { ++cnt; do { v=stack[top--]; instack[v]=false; bel[v]=cnt; }while (v!=u); } } bool judge() { Dindex=top=cnt=0; memset(dfn,0,sizeof(dfn)); for (int i=1;i<=2*n;i++) if (!dfn[i]) tarjan(i); for (int i=1;i<=n;i++) if (bel[i]==bel[i+n]) return false; return true; } int main() { //freopen("J:\\MyDocument\\Code\\input.txt","r",stdin); while (scanf("%d",&n)!=-1) { for (int i=1;i<=n;i++) scanf("%d%d%d%d%d%d",&p[i].x,&p[i].y,&p[i].z,&p[i+n].x,&p[i+n].y,&p[i+n].z); for (int i=1;i<=2*n;i++) for (int j=1;j<=2*n;j++) dis[i][j]=cal(i,j); double l=0, r=10000.0*sqrt(3.0),ans; while (r-l>0.000001) { double mid=(r+l)/2; build(mid); if (judge()) l=mid; else r=mid; } ans=(r+l)/2/2; char tmpstr[200]; sprintf(tmpstr,"%.3f",ans); sscanf(tmpstr,"%lf",&ans); build(ans*2); if(!judge()) ans-=0.001; printf("%.3f\n",ans); } return 0; }