题目链接:[kuangbin带你飞]专题三 Dancing Links B - Treasure Map
题意
给一矩形和k个小矩形,问选取最小数量为多少的小矩形可以对大矩形进行精确覆盖。
思路
仍然是个模版题,把二维的n*m的大矩形看作是一维的n*m的一条线。k个小矩形同理,那么就转化成01矩阵精确覆盖的问题了。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
using namespace std;
const int N = 1009;
const int MAX = 1000009;
int U[MAX], D[MAX], L[MAX], R[MAX];//数组模拟链表指向(上下左右)
int C[MAX], M[MAX];//节点所在列与行
int S[N];//储存每列的元素数量
int H[N];//行头指针
int ans;
void init(int n, int m)
{
for(int i=0; i<=m; i++)
{
L[i+1] = i;
R[i] = i+1;
U[i] = D[i] = i;
S[i] = 0;
}
for(int i=1; i<=n; i++)
H[i] = -1;
L[0] = m;
R[m] = 0;
}
void link(int row, int col, int id)//将节点加入链表
{
C[id] = col; M[id] = row;//记录行列
U[id] = U[col]; D[U[col]] = id;//上下连接
D[id] = col; U[col] = id;
if(H[row] == -1)//左右连接(使用表头方便头插)
H[row] = L[id] = R[id] = id;
else
{
L[id] = L[H[row]]; R[L[H[row]]] = id;
L[H[row]] = id; R[id] = H[row];
}
S[col]++;
}
void remove(int col)//删除列
{
R[L[col]] = R[col];
L[R[col]] = L[col];
for(int i=D[col]; i!=col; 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]]--;
}
}
}
void resume(int col)//恢复列(先删的后恢复,后删的先恢复,所以跟remove反向操作)
{
R[L[col]] = col;
L[R[col]] = col;
for(int i=U[col]; i!=col; i=U[i])
{
for(int j=L[i]; j!=i; j=L[j])
{
U[D[j]] = j;
D[U[j]] = j;
S[C[j]]++;
}
}
}
void dance(int k)
{
if(ans!=-1 && k>=ans)
return;
if(!R[0])
{
ans = k;
return;
}
int col = R[0];
for(int i=R[0]; i!=0; i=R[i])
if(S[i] < S[col])
col = i;
remove(col);
for(int i=D[col]; i!=col; i=D[i])
{
for(int j=R[i]; j!=i; j=R[j])
remove(C[j]);
dance(k+1);
for(int j=L[i]; j!=i; j=L[j])
resume(C[j]);
}
resume(col);
}
int main()
{
int n, m, k, T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &n, &m, &k);
init(k, n*m);
int id = n*m+1;
for(int i=1; i<=k; i++)
{
int x1, x2, y1, 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, y+(x-1)*m, id++);
}
ans = -1;
dance(0);
printf("%d\n", ans);
}
return 0;
}