zoj 3209 矩形精确覆盖(dancing link)

题意 :  给你一个大小为 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<cstdio>  
#define inf 10000000  
#define N 1005  
#define M 1024*505  
int U[M],D[M],L[M],R[M],C[M];//C代表M所属的列,U,D,L,R为一个元素的上下左右指针  
int H[505];//H是水平循环链表的头指针  
int S[1005];//S代表每一列的元素个数  
int size,n,m,p,ans; 

void Link(int r,int c)
{
    U[size] = c;  
    D[size] = D[c];  
    U[D[c]] = size;  
    D[c] = size;  
    if (H[r] < 0)  
        H[r] = L[size] = R[size] = size;  
    else {  
        L[size] = H[r];  
        R[size] = R[H[r]];  
        L[R[H[r]]] = size;  
        R[H[r]] = size;  
    }  
    S[c]++; 
//	row[size] = r ; 
    C[size++] = c; 
}

void Remove(int c) {   //删除c列及相应的行   
    int i, j;  
    R[L[c]] = R[c];  
    L[R[c]] = L[c];  
    for (i = D[c]; i != c; i = D[i]) {  
        for (j = R[i]; j != i; j = R[j]) {  
            U[D[j]] = U[j];  
            D[U[j]] = D[j];  
            S[C[j]]--;  
        }  
    }  
}  
void Resume(int c) {  //恢复c列   
    int i, j;  
    R[L[c]] = c;  
    L[R[c]] = c;  
    for (i = D[c]; i != c; i = D[i]) {  
        for (j = R[i]; j != i; j = R[j]) {  
            U[D[j]] = j;  
            D[U[j]] = j;  
            S[C[j]]++;  
        }  
    }  
}  
void Dance(int now) {   //舞蹈链   
    if (R[0] == 0)  //输出答案   
       { 
	     if(ans > now) //更新最小答案 ; 
	     ans = now ;
        return ;
	   }
    int i, j, temp, c;  
    for (temp=inf,i = R[0]; i; i = R[i]) {  
        if(S[i]<temp)  
        {  
            temp=S[i];  
            c=i;  
        }  
    }  
    Remove(c);  
    for(i=D[c];i!=c;i=D[i])  
    {   // O[now]=row[i]  ;   //记录答案 ;   
        for(j=R[i];j!=i;j=R[j])  
            Remove(C[j]);  
        Dance(now+1)  ;
        for(j=L[i];j!=i;j=L[j])  
            Resume(C[j]);  
    }  
    Resume(c);  
     
}  

void init(int n,int m){
      int i;  
    for (i = 1; i <= n; i++)   
            H[i] = -1;  
    for (i = 0; i <= m; i++) {  
        S[i] = 0;  
        L[i + 1] = i;  
        R[i] = i + 1;  
        U[i] = D[i] = i;  
    }  
    R[m] = 0;  
    size = m + 1;  
} 

int main()  
{  
     int t ,n,m,p,x,y,xx,yy;
	  scanf("%d",&t) ;
	while(t--)
	{
		  scanf("%d%d%d",&n,&m,&p) ;
		  init(p,n*m) ;
		  for(int k = 1 ; k <= p ; k++)
		  {
		  	       scanf("%d%d%d%d",&x,&y,&xx,&yy) ;
					 for(int i = x ; i < xx ; i++)
					   for(int j = y; j < yy ; j++)
					    {
					    	   int tmp = i*m+j+1 ; 
					    	   Link(k,tmp) ; 
					    //	   printf("%d ",tmp) ;
					    } 
					//    puts("") ;
		  }
		  ans = inf ;
		  Dance(0);
		  if(ans != inf) 
		     printf("%d\n",ans) ;
		  else printf("-1\n");
    }  
    return 0;  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值