ZOJ3209 Treasure Map
原题地址:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3209
题意:
T组数据。
给出一个n*m的矩形,给出p个小矩形(所在的左下角和右上角的坐标),求覆盖大矩形至少需要多少个小矩形(小矩形间不能重复覆盖)
数据范围
T <= 500,1 <= n, m <= 30, 1 <= p <= 500
0 <= x1 < x2 <= n, 0 <= y1 < y2 <= m
题解:
一篇好的DLX教程
这题就是把大矩形拆成n*m个小格子,就是DLX的列数,每个小矩形是一行,相应的位置加上点。
因为要求最小答案,还是要暴搜的,于是要剪枝,每次从残余行最少的列开始。
精确覆盖:
首先选择当前要覆盖的列(含1最少的列),将该列和能够覆盖到该列的行全部去掉,再枚举添加的方法。
枚举某一行r,假设它是解集中的一个,那么该行所能覆盖到的所有列都不必再搜,所以删除该行覆盖到的所有列,又由于去掉的列相当于有解,所以能够覆盖到这些列的行也不用再搜,删之。
重复覆盖:
首先选择当前要覆盖的列(同上),将该列删除,枚举覆盖到该列的所有行:对于某一行r,假设它是解集中的一个,那么该行所能覆盖到的列都不必再搜,所以删除该行覆盖到的所有列。
注意此时不用删去覆盖到这些列的行,因为一列中允许有多个1。
——摘自此博客
因为DLX本质上是个暴力,而重复覆盖的剪枝力度更小,重复覆盖往往需要估价函数来优化。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=500000;
const int M=505;
int T;
int R[N],L[N],U[N],D[N],S[N],H[M],col[N],row[N],tail=0,ans;
void init(int n,int m)
{
for(int i=0;i<=m;i++)
{R[i]=i+1; L[i+1]=i;U[i]=D[i]=i;S[i]=0;}
L[0]=m; R[m]=0;
for(int i=1;i<=n;i++) H[i]=-1;
}
void link(int r,int c)
{
int nd=++tail;
row[nd]=r; col[nd]=c;
D[U[c]]=nd; U[nd]=U[c]; D[nd]=c; U[c]=nd;
if(H[r]==-1) {H[r]=nd; L[nd]=R[nd]=nd;}
else
{R[L[H[r]]]=nd;L[nd]=L[H[r]];L[H[r]]=nd; R[nd]=H[r];}
S[c]++;
}
void remove(int x)
{
R[L[x]]=R[x]; L[R[x]]=L[x];
for(int i=D[x];i!=x;i=D[i])
{
for(int j=R[i];j!=i;j=R[j])
{
D[U[j]]=D[j]; U[D[j]]=U[j];
S[col[j]]--;
}
}
}
void resume(int x)
{
R[L[x]]=x; L[R[x]]=x;
for(int i=U[x];i!=x;i=U[i])
{
for(int j=L[i];j!=i;j=L[j])
{
D[U[j]]=j; U[D[j]]=j;
S[col[j]]++;
}
}
}
void DLX(int k)
{
if(ans!=-1&&k>ans) return;
if(!R[0]){ans=k; return;}
int pos=R[0];
for(int i=R[0];i;i=R[i])
if(S[i]<S[pos]) pos=i;
remove(pos);
for(int i=D[pos];i!=pos;i=D[i])
{
for(int j=R[i];j!=i;j=R[j])
remove(col[j]);
DLX(k+1);
for(int j=L[i];j!=i;j=L[j])
resume(col[j]);
}
resume(pos);
}
int main()
{
scanf("%d",&T);
while(T--)
{
int n,m,p; scanf("%d%d%d",&n,&m,&p);
init(p,n*m);tail=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+1;x<=X2;x++)
for(int y=Y1+1;y<=Y2;y++)
link(i,(x-1)*m+y);
}
ans=-1;
DLX(0);
printf("%d\n",ans);
}
return 0;
}