Time Limit: 10000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2754 Accepted Submission(s): 918
给出一些点对,你可以在每对中任意选一个,只能选一个,放置一个炸弹,每个炸弹爆炸时都有一个效果范围,会波及到其放置点为圆心,半径为 r 的圆的范围,问如果要让任意两个圆都不相交(可以相切)的话,半径的最大值是多少。
很裸的 2-SAT 模型,每组点分为 A 和 A‘ ,然后2分枚举半径的值,如果两点间距离小于半径的二倍,那么这两点不能同时放置炸弹,也就是说他们矛盾,根据这个关系建边,判断是否存在解,如果存在说明半径还可以继续增大,否则,半径要减小。
PS:这个题的精度问题,输出要求的是10的负二次方,但是精度只开到 1e-3 会wa
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int VM=210; const int EM=40010; const double eps=1e-8; struct Edge{ int to,nxt; }edge[EM<<1]; int n,m,cnt,dep,top,atype,head[VM]; int dfn[VM],low[VM],vis[VM],belong[VM]; int stack[VM]; double point[VM][2]; void Init(){ cnt=0, atype=0, dep=0, top=0; memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(belong,0,sizeof(belong)); } void addedge(int cu,int cv){ edge[cnt].to=cv; edge[cnt].nxt=head[cu]; head[cu]=cnt++; } void Tarjan(int u){ dfn[u]=low[u]=++dep; stack[top++]=u; vis[u]=1; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(vis[v]) low[u]=min(low[u],dfn[v]); } int j; if(dfn[u]==low[u]){ atype++; do{ j=stack[--top]; belong[j]=atype; vis[j]=0; }while(u!=j); } } bool CalDis(int i,int j,double mid){ return (point[i][0]-point[j][0])*(point[i][0]-point[j][0])+(point[i][1]-point[j][1])*(point[i][1]-point[j][1])-4*mid*mid<eps; } int solve(double mid){ for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(CalDis(i,j,mid)){ addedge(i,j+n); addedge(j,i+n); } if(CalDis(i,j+n,mid)){ addedge(i,j); addedge(j+n,i+n); } if(CalDis(i+n,j,mid)){ addedge(i+n,j+n); addedge(j,i); } if(CalDis(i+n,j+n,mid)){ addedge(i+n,j); addedge(j+n,i); } } for(int i=1;i<=2*n;i++) if(!dfn[i]) Tarjan(i); for(int i=1;i<=n;i++) if(belong[i]==belong[i+n]) return 0; return 1; } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d",&n)){ for(int i=1;i<=n;i++) scanf("%lf%lf%lf%lf",&point[i][0],&point[i][1],&point[i+n][0],&point[i+n][1]); double l=0,r=100000,mid,ans=0; while(l+eps<=r){ Init(); //因为二分每次都得重新建边,所以初始化在这里 mid=(l+r)/2; if(solve(mid)){ //return true,说明边太少了,应该增大mid,所以l = mid l=mid; ans=mid; //ans=max(ans,mid); //printf("ans=%.2lf\n",ans); }else //return false,说明边太多了,应该减小mid,所以r = mid r=mid; } printf("%.2lf\n",ans); } return 0; }