现在的位置: 首页 > 算法 > 正文

zoj – 3209 – Treasure Map(精确覆盖DLX)

2019年08月30日 算法 ⁄ 共 2065字 ⁄ 字号 评论关闭

题意:一个 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;
}

抱歉!评论已关闭.