标签:dlx dancing link
题意 : 有 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 ; }
HDU 2295 Radar( 二分+Dancing Links重复覆盖 )
标签:dlx dancing link
原文地址:http://blog.csdn.net/lishaozhe1024/article/details/39759639