题意:给出一些城市及一些雷达的坐标,要求从这些雷达中选取最多k个能够覆盖所有的城市,问雷达的最小覆盖半径为多少。
二分半径,则可转化为一个判定问题,即给定雷达的覆盖半径,问从这些雷达中最多选取k个,是否能够覆盖所有这些城市。、
这里用到了Dancing Links 解决重复覆盖问题的方法来进行判断,与精确覆盖问题的不同之处在于,Remove中只删除当前列,而不是把所有与之相关的行及列全部删去,用
Resume恢复即可,另,由于种类型题目的限制,Dancing Links的效率不高,需要在搜索中加上启发式判断,于是就有了另一个函数的 h() 设计,使效率大为提高。
Code:
#include<stdio.h> #include<string.h> #include<stdlib.h> typedef struct{ double x,y; }Point; #define M 64 const int head=0; const int V=M*M; const double eps=1e-7; int R[V],L[V],U[V],D[V],C[V]; int S[V],H[V],size; int n,m; int Limit; Point radar[M]; Point city[M]; double Dis2(Point p,Point q) { p.x-=q.x;p.y-=q.y; return p.x*p.x+p.y*p.y; } void Remove(int c) { int i;//只删除该列 for(i=D[c];i!=c;i=D[i]) L[R[i]]=L[i],R[L[i]]=R[i]; } void Resume(int c) { int i; for(i=U[c];i!=c;i=U[i]) L[R[i]]=R[L[i]]=i; } int h() { int r=0,i,j,k; int hash[M]; memset(hash,0,sizeof(hash)); for(i=R[head];i!=head;i=R[i]){ if(!hash[i]){ r++; hash[i]=1; for(j=D[i];j!=i;j=D[j]){ for(k=R[j];k!=j;k=R[k]) hash[C[k]]=1; } } } return r; } int Dance(int k) { int i,j,min,c; if(k+h()>Limit) return 0; if(R[head]==head)return 1; for(i=R[head],c=0,min=M;i!=head;i=R[i]){ if(S[i]<min) min=S[i],c=i; } for(i=D[c];i!=c;i=D[i]){ Remove(i); for(j=R[i];j!=i;j=R[j]) Remove(j); if(Dance(k+1)) return 1; for(j=L[i];j!=i;j=L[j]) Resume(j); Resume(i); } return 0; } void Link(int &r,int c) { S[c]++;C[size]=c; U[size]=U[c];D[U[c]]=size; D[size]=c;U[c]=size; if(r==-1) L[size]=R[size]=r=size; else{ L[size]=L[r];R[L[r]]=size; R[size]=r;L[r]=size; }size++; } int Solve(double rr) { int i,j; for(i=0;i<=n;i++){ S[i]=0;U[i]=D[i]=i; R[i]=i+1;L[i+1]=i; }R[n]=0; size=n+1; memset(H,-1,sizeof(H)); for(i=0;i<m;i++){ for(j=0;j<n;j++){ if(Dis2(radar[i],city[j])<=rr) Link(H[i],j+1); } } for(i=1;i<=n;i++){ if(S[i]==0) return 0; } return Dance(0); } int main() { int t,i; double up,low,mid; scanf("%d",&t); while(t--){ scanf("%d%d%d",&n,&m,&Limit); for(i=0;i<n;i++) scanf("%lf%lf",&city[i].x,&city[i].y); for(i=0;i<m;i++) scanf("%lf%lf",&radar[i].x,&radar[i].y); low=0.000001;up=1416.0; while(low+eps<up){ mid=(low+up)*0.5; if(Solve(mid*mid)) up=mid; else low=mid; } printf("%lf\n",up); } return 0; }