这是练手的一道dancing links题,直接套用上篇博文那题的代码即可,不过得把问题稍微转化以下。
地图是个m行n列的地图,即有n*m个小块,共有p个图片去覆盖,那么我们只要找出每个小块和p个图片的关系,构造一个p行n*m列的矩阵,那么第j行第j列表示,j对应的那个小块能被图片i覆盖
剩下的问题,就是如何选出最少的行数使得每列被覆盖即可(可以参见上一篇文章 http://blog.youkuaiyun.com/cen5bin/article/details/9713089)
附上代码:
#include <cstdio>
#include <cstring>
const int M = 1010;
const int N = M*M;
int R[N],L[N],U[N],D[N],C[N];
int S[M];
int n,m;
int ans; // 需要拿掉几行
int size; //节点个数
void init()
{
memset(S,0,sizeof(S)); //初始的时候每列上1的个数为0
//链表初始化
for(int i=1; i<=m; i++)
L[i+1] = R[i-1] = U[i] = D[i] = i;
R[m] = 0; //0表示表头
size = m + 1; //新的节点的下标是从m+1开始的,当然也可以随机的设置一个更大的值,没有影响
}
//删除第c列,以及该列中元素对应的所有行
void remove(int c)
{
R[L[c]] = R[c];
L[R[c]] = L[c];
for(int i=D[c]; i!=c; i=D[i])
for(int j=R[i]; j!=i; j=R[j])
{
U[D[j]] = U[j];
D[U[j]] = D[j];
S[C[j]]--;
}
}
//恢复第c列,以及该列中元素对应的所有行
void resume(int c)
{
for(int i=U[c]; i!=c; i=U[i])
for(int j=L[i]; j!=i; j=L[j])
{
U[D[j]] = D[U[j]] = j;
S[C[j]]++;
}
R[L[c]] = L[R[c]] = c;
}
void dfs(int k) //k表示当前已经拿掉的行数
{
if(R[0]==0) //表示所有的列已经被拿完
{
if(ans==-1||ans>k) ans = k;
return;
}
int min = M,c=-1;
//选取元素个数最少的列
for(int i=R[0]; i!=0; i=R[i])
if(S[i]<min) min = S[i],c = i;
remove(c);
for(int i=D[c]; i!=c; i=D[i])
{
for(int j=R[i]; j!=i; j=R[j]) remove(C[j]);
dfs(k+1);
for(int j=L[i]; j!=i; j=L[j])
resume(C[j]);
}
resume(c);
}
void insertCol(int col)
{
C[size] = col;
U[size] = U[col];
D[U[col]] = size;
U[col] = size;
D[size] = col;
S[col]++;
}
void insertRow(int &rowhead)
{
if(rowhead==-1)
{
L[size] = R[size] = size;
rowhead = size;
}
else
{
L[size] = L[rowhead];
R[L[rowhead]] = size;
L[rowhead] = size;
R[size] = rowhead;
}
}
int main()
{
//freopen("in","r",stdin);
int t;
scanf("%d",&t);
while(t--)
{
int a,b;
scanf("%d%d%d",&a,&b,&n);
m = a * b;
init();
for(int i=1; i<=n; i++)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1++,y1++;
int rowhead = -1;
for(int j=x1; j<=x2; j++)
for(int k=y1; k<=y2; k++)
{
int col = j + (k-1)*a;
insertCol(col);
insertRow(rowhead);
size++;
}
}
ans = -1;
dfs(0);
printf("%d\n", ans);
}
return 0;
}