题意:一个 n x m 的矩形(1 <= n, m <= 30),现给出这个矩形中 p 个(1 <= p <= 500)子矩形的左下角与右下角坐标,问最少用多少个子矩形可以恰好组成这个 n x m 的大矩形。
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3372
——>>这是精确覆盖问题,而DLX正是解决精确覆盖问题的有利武器。。
模型转换:将原矩形变成一行,作为 DLX 中的列,表示要被覆盖一次且仅一次的目标。子矩形则是行中的一些点,每个子矩形作为 DLX 的一行,它所覆盖行中的点的位置标为1,其余位标为0,则问题转化为:选出最少的行,使得选出的行中每一列有且仅有一个1,这正是 DLX 解决的问题。。
注:在往下一层 dfs 的时候,如果检测返回值为真,可别来个return true,这时会中断后面的搜索(很隐秘地 WA 了几发)。。为了方便,我不要返回值了。。
为了加快搜索,可以来个剪枝,dfs 时检测当前深度是否 >= 已搜索到的可满足要求的长度。。
#include <cstdio>
#include <cstring>const int MAXN = 30 + 10;
const int MAXR = 500 + 10;
const int MAXC = 30 * 30 + 10;
const int MAXNODE = MAXR * MAXC;
const int INF = 0x3f3f3f3f;struct DLX
{int sz;int H[MAXR], S[MAXC];int row[MAXNODE], col[MAXNODE];int U[MAXNODE], D[MAXNODE], L[MAXNODE], R[MAXNODE];int Min;void Init(int n){for (int i = 0; i <= n; ++i){U[i] = D[i] = i;L[i] = i - 1;R[i] = i + 1;}L[0] = n;R[n] = 0;sz = n + 1;memset(S, 0, sizeof(S));memset(H, -1, sizeof(H));}void Link(const int& r, const int& c){row[sz] = r;col[sz] = c;D[sz] = D[c];U[D[c]] = sz;D[c] = sz;U[sz] = c;if (H[r] == -1){H[r] = L[sz] = R[sz] = sz;}else{R[sz] = R[H[r]];L[R[H[r]]] = sz;R[H[r]] = sz;L[sz] = H[r];}S[c]++;sz++;}void Remove(const int& c){L[R[c]] = L[c];R[L[c]] = R[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[col[j]]--;}}}void Restore(const int& c){for (int i = U[c]; i != c; i = U[i]){for (int j = L[i]; j != i; j = L[j]){S[col[j]]++;U[D[j]] = j;D[U[j]] = j;}}L[R[c]] = c;R[L[c]] = c;}void Dfs(int cur){if (cur >= Min) return;if (R[0] == 0){if (cur < Min){Min = cur;}return;}int c = R[0];for (int i = R[0]; i != 0; i = R[i]){if (S[i] < S[c]){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(col[j]);}Dfs(cur + 1);for (int j = L[i]; j != i; j = L[j]){Restore(col[j]);}}Restore(c);}void Solve(){Min = INF;Dfs(0);Min != INF ? printf("%d\n", Min) : puts("-1");}} dlx;int ReadInt()
{int ret = 0;char ch;while ((ch = getchar()) && ch >= '0' && ch <= '9'){ret = ret * 10 + ch - '0';}return ret;
}void Read()
{int n, m, p;scanf("%d%d%d", &n, &m, &p);dlx.Init(n * m);getchar();for (int i = 1; i <= p; ++i){int x1 = ReadInt();int y1 = ReadInt();int x2 = ReadInt();int y2 = ReadInt();for (int j = y1 + 1; j <= y2; ++j){for (int k = x1 + 1; k <= x2; ++k){dlx.Link(i, (j - 1) * n + k);}}}
}int main()
{int T;scanf("%d", &T);while (T--){Read();dlx.Solve();}return 0;
}