首页 > 代码库 > ZOJ 3209 Treasure Map (Dancing Links 精确覆盖 )

ZOJ 3209 Treasure Map (Dancing Links 精确覆盖 )

题意 :  给你一个大小为 n * m 的矩形 , 坐标是( 0 , 0 ) ~ ( n , m )  。然后给你 p 个小矩形 , 坐标是( x1 , y1 ) ~ ( x2 , y2 ) , 你选择最小的几个矩形 , 使得这些矩形可以覆盖整个矩形 , 并且互相不会重叠 。( n , m <= 30 )

思路 : Dancing Links 的精确覆盖问题 。

我们将 n * m 的矩形分成 n * m 个小正方形 ,那么我们只要保证每个小正方形被覆盖且只被覆盖一次即可 。

那么列表示每个小正方形 , 行表示你选择的矩形 , 如果选择这个矩形可以覆盖某个小正方形 , 则对应的格子是1 , 否则对应的格子是 0

最多有 30 * 30 = 900 列 ,最多有 500 行


#include <stdio.h>  
#include <string.h>  
#include <algorithm>  
#include <vector>  
using namespace std;  

const int maxn = 900 + 10 ;  
const int maxr = 500 + 10 ;  
const int maxnode = 500 * 900 + 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 ) {  
		// 删除虚拟结点中的 c 列  
		L[R[c]] = L[c] ;  
		R[L[c]] = R[c] ;  
		// 从 c 列向下遍历  
		FOR( i , D , c ) {  
			// 删除遍历到的行  
			FOR( j , R , i ) {  
				D[U[j]] = D[j] ;  
				U[D[j]] = U[j] ;  
				-- S[col[j]] ;  
			}  
		}  
	}  

	// 恢复第 c 列  
	void restore( int c ) {  
		FOR( i , U , c ) {  
			FOR( j , L , i ) {  
				++S[col[j]] ;  
				U[D[j]] = D[U[j]] = j ;  
			}  
		}  
		L[R[c]] = R[L[c]] = c ;  
	}  

	
	void dfs( int d ) {

		// 剪枝
		if( d >= best ) {
			return ;
		}

		// R[0] = 0 表示找到一个可行解
		if( R[0] == 0 ) {  
			best = d ;
			return ;  
		}  

		// 找到 s 最小的列 , 加快搜索的速度  
		int c = R[0] ;  
		FOR( i , R , 0 )
			if( S[i] < S[c] ) c = i ;

		// 删除第 c 列  
		remove( c ) ;  

		// 遍历选中列中有1的行  
		FOR( i , D , c ) {  
			ans[d] = row[i] ;  
			// 删除选中行中有1的列  
			FOR( j , R , i ) {  
				remove( col[j] ) ;  
			}  
			dfs( d + 1 ) ;  
			// 回复删除掉的列  
			FOR( j , L , i ) {  
				restore( col[j] ) ;  
			}  
		}  
		restore( c ) ;  
	}  

	int solve() {  
		best = INF ;
		dfs( 0 ) ;
		if( best == INF ) 
			return -1 ;
		else
			return best ;
	}  
	int best ;
	const static int INF = 0x3f3f3f3f ;
} dlx ;  

int main(){
	int cas ;
	scanf( "%d" , &cas ) ;
	while( cas -- ) {
		int n , m , p ;
		scanf( "%d%d%d" , &n , &m , &p ) ;
		dlx.init( n * m ) ;
		for( int i = 1 ; i <= p ; i ++ ) {
			int x1 , y1 , x2 , y2 ;
			scanf( "%d%d%d%d" , &x1 , &y1 , &x2 , &y2 ) ;
			for( int x = x1 ; x < x2 ; x ++ ) {
				for( int y = y1 ; y < y2 ; y ++ ) {
					dlx.Link( i , x * m + y + 1 ) ;
				}
			}
		}
		printf( "%d\n" , dlx.solve() ) ;
	}
	return 0 ;
}





ZOJ 3209 Treasure Map (Dancing Links 精确覆盖 )