题意:
给定n个机器人的坐标,让确定m个维修点的坐标,使得机器人到离他最近的维修点的最大距离最小化。
分析:
这道题目于以往不一样的地方在于以往的是m个点在n个点上,所以直接建图就可以的。但是这道题让自己确定m个点。所以采用的解决方法是二分枚举圆的半径,从而确定圆心,再根据圆心判断每个圆心能到达哪些点。但是要注意去重,因为很多时候这个圆能覆盖到的是另一个圆能覆盖到的子集。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int maxm=1005;
const int maxnode=maxn*maxm;
const int inf=0x3f3f3f3f;
int N,M;
struct DLX{
int n,m,size;
int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
int H[maxn],S[maxm];
int ansd,ans[maxn];
void init(int _n,int _m){
n=_n;
m=_m;
for(int i=0;i<=m;i++){
S[i]=0;
U[i]=D[i]=i;
L[i]=i-1;
R[i]=i+1;
}
R[m]=0;L[0]=m;
size=m;
for(int i=1;i<=n;i++){
H[i]=-1;
}
}
void Link(int r,int c){
++S[Col[++size]=c];
Row[size]=r;
D[size]=D[c];
U[D[c]]=size;
U[size]=c;
D[c]=size;
if(H[r]<0) H[r]=L[size]=R[size]=size;
else{
R[size]=R[H[r]];
L[R[H[r]]]=size;
L[size]=H[r];
R[H[r]]=size;
}
}
void remove(int c){
for(int i=D[c];i!=c;i=D[i]){
L[R[i]]=L[i];
R[L[i]]=R[i];
}
}
void resume(int c){
for(int i=U[c];i!=c;i=U[i]){
L[R[i]]=R[L[i]]=i;
}
}
bool v[maxm];
int f(){
int ret=0;
for(int c=R[0];c!=0;c=R[c]) v[c]=1;
for(int c=R[0];c!=0;c=R[c]){
if(v[c]){
ret++;
v[c]=false;
for(int i=D[c];i!=c;i=D[i]){
for(int j=R[i];j!=i;j=R[j]){
v[Col[j]]=false;
}
}
}
}
return ret;
}
bool dance(int d){
if(d+f()>M) return false;
if(R[0]==0){
return d<=M;
}
int c=R[0];
for(int i=R[0];i!=0;i=R[i]){
if(S[i]<S[c]){
c=i;
}
}
for(int i=D[c];i!=c;i=D[i]){
remove(i);
for(int j=R[i];j!=i;j=R[j]) remove(j);
if(dance(d+1)) return true;
for(int j=L[i];j!=i;j=L[j]) resume(j);
resume(i);
}
return false;
}
}dlx;
struct point{
double x,y;
point(){}
point(double x,double y):x(x),y(y){}
point operator - (const point p){ return point(x-p.x,y-p.y); }
point operator + (const point p){ return point(x+p.x,y+p.y); }
point operator * (double s){ return point(x*s,y*s); }
point operator / (double s){ return point(x/s,y/s); }
double _distance(){ return x*x+y*y; }
}pt[20],center[550];
double distance(point &a,point &b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double dis[20][20];
const double eps=1e-8;
int cmp(double x){
if(fabs(x)<eps) return 0;
if(x>0) return 1;
return -1;
}
void mark(point p1,point p2,double d,double r,int &cnt){
if(cmp(d)==0){
center[++cnt]=p1;
return ;
}
point p0=(p1+p2)/2;
if(cmp(d-2*r)==0) center[++cnt]=p0;
if(cmp(d-2*r)==-1){
point p=(p2-p1)/d,v=point(p.y,-p.x);
p=p*d/2;
v=v*sqrt(r*r-p._distance());
center[++cnt]=p0+v;
center[++cnt]=p0-v;
}
}
int ST[550],vis[550];
bool judge(double mid){
int cnt=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
mark(pt[i],pt[j],dis[i][j],mid,cnt);
}
}
dlx.init(cnt,N);
double r=mid*mid;
memset(vis,0,sizeof vis);
memset(ST,0,sizeof ST);
for(int i=1;i<=cnt;i++){
for(int j=1;j<=N;j++){
if(cmp((center[i]-pt[j])._distance()-r)<=0) ST[i]|=(1<<j);
}
}
for(int i=1;i<=cnt;i++){
if(vis[i]==0){
for(int j=i+1;j<=cnt;j++){
if((ST[i]|ST[j])==ST[j]){
vis[i]=1;
break;
}
if((ST[i]|ST[j])==ST[i]){
vis[j]=1;
}
}
}
}
for(int i=1;i<=cnt;i++){
for(int j=1;vis[i]==0&&j<=N;j++){
if(ST[i]&(1<<j)) dlx.Link(i,j);
}
}
return dlx.dance(0);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++){
scanf("%lf%lf",&pt[i].x,&pt[i].y);
}
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
dis[i][j]=distance(pt[i],pt[j]);
}
}
double l=0.0,r=15.0;
while(cmp(r-l)==1){
double mid=(l+r)/2;
if(judge(mid)) r=mid;
else l=mid;
}
printf("%.6f\n",r);
}
return 0;
}