题意 : 有 n 个城市 , m个站 , 你要选择 k 个站 , 每个站画一个半径为 r 的圆 , 可以覆盖所有的城市 , 一个城市可以被多个站覆盖 。求的是满足要求的最小的 r 。
思路很明显了 , 首先是二分 , 将问题转化成可行性判定的问题 。
那么对于 mid , 我们将 站看成行 , 将城市看成 列 , 如果一个站和一个城市的距离小于mid , 那么对应的矩阵位置的值就1 , 否则是0 , 用dlx 重复覆盖来解决
重复覆盖和精确覆盖主要有两个区别 :
第一, remove 和 restore 两个函数有区别 , 重复覆盖在删除列的时候 , 不用删除行
第二,就是因为不删除行 ,矩阵变稀疏的速度也就会减慢 , 需要用启发式函数来剪枝
启发式函数的做法是选择一个列 , 删除( 其实就是遍历 ) 掉这一列为1的所有行 , 然后再标记掉这些行中有1的列。
这里删除掉多行的代价只有1 , 所以会比真实的花费要少 ,如果当前深度加上这个已经优于最优解的估价还不可行,那就肯定可以剪掉了。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <math.h>
using namespace std;
const int maxn = 55 + 10 ;
const int maxr = 55 + 10 ;
const int maxnode = 55 * 55 + maxr + 10 ;
#define FOR( i , A , s ) for( int i = A[s] ; i != s ; i = A[i] )
struct DLX{
// maxn 列数 , maxnode 总节点数 , maxr 行数
int n , sz ;
int S[maxn] ;
int row[maxnode] , col[maxnode] ;
int L[maxnode] , R[maxnode] , U[maxnode] , D[maxnode] ;
int H[maxr] ;
int ansd , ans[maxr] ;
void init( int N ) {
n = N ;
// 第一行的虚拟结点
for( int i = 0 ; i <= n ; i ++ ) {
U[i] = D[i] = i ;
L[i] = i - 1 ;
R[i] = i + 1 ;
}
R[n] = 0 ; L[0] = n ;
sz = n + 1 ;
// 每一列的个数
memset( S , 0 , sizeof(S) ) ;
// H[i] = -1 表示这一行还没有 1
// 否则表示第一个 1 的 sz 是多少
memset( H , -1 , sizeof(H)) ;
}
// 在第r行第c列添加一个1
void Link( int r , int c ) {
row[sz] = r ;
col[sz] = c ;
S[c] ++ ;
D[sz] = c ; U[sz] = U[c] ;
D[U[c]] = sz ; U[c] = sz ;
if( H[r] < 0 ) { H[r] = L[sz] = R[sz] = sz ; }
else{
R[sz] = H[r] ;
L[sz] = L[H[r]] ;
R[L[sz]] = sz ;
L[R[sz]] = sz ;
}
sz ++ ;
}
// 删除 第 c 列
void remove ( int c ) {
FOR( i , D , c ) {
L[R[i]] = L[i] ;
R[L[i]] = R[i] ;
}
}
// 恢复第 c 列
void restore( int c ) {
FOR( i , D , c ) {
L[R[i]] = R[L[i]] = i ;
}
}
bool vis[maxn] ;
int f() {
int ans = 0 ;
FOR( i , R , 0 ) vis[i] = true ;
FOR( i , R , 0 ) {
if( vis[i] ) {
vis[i] = false ;
ans ++ ;
FOR( j , D , i )
FOR( k , R , j )
vis[col[k]] = false ;
}
}
return ans ;
}
bool dfs( int d ) {
// 剪枝
if( d + f() > best ) return false ;
// R[0] = 0 表示找到一个可行解
if( R[0] == 0 ) return d <= best ;
// 找到 s 最小的列 , 加快搜索的速度
int c = R[0] ;
FOR( i , R , 0 )
if( S[i] < S[c] ) c = i ;
FOR( i , D , c ) {
remove(i);
FOR( j , R , i ) remove( j );
if (dfs(d + 1))
return true ;
FOR( j , R , i ) restore( j ) ;
restore( i ) ;
}
return false ;
}
bool solve( int k ) {
best = k ;
return dfs(0) ;
}
int best ;
} dlx ;
int n , m , k ;
struct Point{
double x , y ;
void get(){
scanf( "%lf%lf" , &x , &y ) ;
}
friend double dist ( const Point &a , const Point & b ) {
return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) ) ;
}
};
Point city[55] ;
Point station[55] ;
int main(){
int cas ;
scanf( "%d" , &cas ) ;
while( cas -- ) {
scanf( "%d%d%d" , &n , &m , &k ) ;
for( int i = 1 ; i <= n ; i ++ ) {
city[i].get() ;
}
for( int i = 1 ; i <= m ; i ++ ) {
station[i].get() ;
}
double l = 0.0 , r = 2000.0 ;
double mid ;
while( r - l > 1e-8 ) {
mid = ( l + r ) / 2.0 ;
dlx.init( n ) ;
for( int i = 1 ; i <= m ; i ++ ) {
for( int j = 1 ; j <= n ; j ++ ) {
if( dist( station[i] , city[j] ) <= mid ) {
dlx.Link( i , j ) ;
}
}
}
if( dlx.solve( k ) ) {
r = mid ;
}else{
l = mid ;
}
}
printf( "%lf\n" , mid ) ;
}
return 0 ;
}