题意:有n个城市,m个工作站,工作站的覆盖区域呈圆状,且有相同的半径,最多只能使用m个工作站,问可以覆盖掉所有的城市的工作站半径最小为多少。
思路:二分加DLX。采用二分半径的方法,对于每一个半径,构造一个十字链,列为n个城市,行为m个工作站,对于每一行来讲为当前半径下,工作站可以覆盖掉的城市。然后进行A*即可。
tirick:1)一个是对于选取最小行时,设置的最大值比较小。WA。
2)Astar时从下往上迭代TLE,从上往下迭代AC。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define inf 0x3f3f3f
#define eps 1e-7//精度控制范围
using namespace std;
const int maxn=64*64+64;
struct nn
{
int x;
int y;
};
nn p[100];
nn p2[100];
struct node2
{
int l,r;
int u,d;
int s,c;
};
node2 dlx[maxn];
int h[64];
int head;
int size;
bool hash[64];
int limit;
int n,m;
double dis(nn a,nn b)
{
return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool check(nn a,nn b,double r)//判断在不在圆内
{
double tmp;
tmp=dis(a,b);
if(tmp<=r)
{
return true;
}
return false;
}
void init(int r,int c)// 初始化
{
head=0;
for(int i=0; i<=c; i++)
{
dlx[i].s=0;
dlx[i].u=dlx[i].d=i;
dlx[i].r=i+1;
dlx[i+1].l=i;
}
size=c;
dlx[c].r=0;
while(r)
{
h[r--]=-1;
}
}
void link(int r,int c)
{
int tmp=++size;
dlx[c].s++;
dlx[tmp].c=c;
dlx[tmp].d=dlx[c].d;
dlx[tmp].u=c;
dlx[dlx[c].d].u=tmp;
dlx[c].d=tmp;
if(h[r]<0)
{
h[r]=dlx[tmp].l=dlx[tmp].r=tmp;
}
else
{
dlx[tmp].r=dlx[h[r]].r;
dlx[tmp].l=h[r];
dlx[dlx[h[r]].r].l=tmp;
dlx[h[r]].r=tmp;
}
}
void remove(int x)
{
for(int i=dlx[x].d; i!=x; i=dlx[i].d)
{
dlx[dlx[i].r].l=dlx[i].l;
dlx[dlx[i].l].r=dlx[i].r;
dlx[dlx[i].c].s--;
}
}
void resume(int x)
{
for(int i=dlx[x].u; i!=x; i=dlx[i].u)
{
dlx[dlx[i].r].l=i;
dlx[dlx[i].l].r=i;
dlx[dlx[i].c].s++;
}
}
int hh()
{
int ret=0;
memset(hash,0,sizeof(hash));
for(int i=dlx[head].r; i!=head; i=dlx[i].r)
{
if(hash[i]==false)
{
hash[i]=true;
ret++;
for(int j=dlx[i].d; j!=i; j=dlx[j].d)
for(int k=dlx[j].r; k!=j; k=dlx[k].r)
{
hash[dlx[k].c]=true;
}
}
}
//cout<<"er"<<endl;
return ret;
}
bool dlx_dfs(int k)//舞蹈链
{
if(k+hh()>limit)
{
return false;
}
if(dlx[head].r==head)
{
if(k<limit) limit=k;
return true;
}
int minn=m+1;
int tt;
for(int i=dlx[head].r; i!=head; i=dlx[i].r)
{
if(minn>dlx[i].s)
{
minn=dlx[i].s;
tt=i;
}
}
for(int i=dlx[tt].d; i!=tt; i=dlx[i].d)
{
remove(i);
for(int j=dlx[i].r; j!=i; j=dlx[j].r)
{
remove(j);
}
if(dlx_dfs(k+1))
{
return true;
}
for(int j=dlx[i].l; j!=i; j=dlx[j].l)
{
resume(j);
}
resume(i);
}
return false;
}
int astar(int x)//astar迭代
{
limit=x;//限制为最大的K。
while(dlx_dfs(0)==true)
{
limit--;
if(limit==0)
{
break;
}
}
if(limit==x) return x+1;
else return limit+1;
}
void pre(double r)
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(check(p[j],p2[i],r))
{
link(i,j);
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
int k;
while(t--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&p2[i].x,&p2[i].y);
}
double l,r;
l=0.000001;
r=1416.0;
int tt;
while(l+eps<=r)//二分答案
{
double mid=(l+r)*0.5;
init(m,n);
pre(mid*mid);
tt=astar(k);
if(tt<=k)
{
r=mid;
}
else
{
l=mid;
}
}
printf("%.6f\n",r);
}
return 0;
}