码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 2295 Radar( 二分+Dancing Links重复覆盖 )

时间:2014-10-03 23:38:09      阅读:193      评论:0      收藏:0      [点我收藏+]

标签: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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!