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
" , dlx.solve() ) ;
	}
	return 0 ;
}





原文地址:https://www.cnblogs.com/liguangsunls/p/6853356.html