poj - 3740 - Easy Finding(精确覆盖DLX)

题意:M行N列,各元素值要么是0,要么是1,问是否可以取出其中的一些行,使取出的行组合中每列有且仅有一个1。

题目链接:http://poj.org/problem?id=3740

——>>2014.10.30 再次更新

      DLX 舞蹈链模板题。。

#include <cstdio>
#include <cstring>

const int MAXM = 16 + 10;
const int MAXN = 300 + 10;
const int MAXNODE = MAXM * MAXN;

struct DLX
{
    int sz;
    int H[MAXM], S[MAXN];
    int row[MAXNODE], col[MAXNODE];
    int U[MAXNODE], D[MAXNODE], L[MAXNODE], R[MAXNODE];

    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;
    }

    bool Dfs()
    {
        if (R[0] == 0)
        {
            return true;
        }

        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]);
            }
            if (Dfs()) return true;
            for (int j = L[i]; j != i; j = L[j])
            {
                Restore(col[j]);
            }
        }
        Restore(c);

        return false;
    }

    bool Solve()
    {
        return Dfs();
    }

} dlx;

int M, N;

void Read()
{
    char ch;

    dlx.Init(N);
    for (int i = 1; i <= M; ++i)
    {
        for (int j = 1; j <= N; ++j)
        {
            getchar();
            ch = getchar();
            if (ch == '1')
            {
                dlx.Link(i, j);
            }
        }
    }
}

void Solve()
{
    dlx.Solve() ? puts("Yes, I found it") : puts("It is impossible");
}

int main()
{
    while (scanf("%d%d", &M, &N) != EOF)
    {
        Read();
        Solve();
    }

    return 0;
}



——>>寒假时刷这题费了超大精力,现在再刷一次,理解起来快多了。

思想:用位运算来加速。

row[i]表示第i行的二进制移位编号,第i行就将1左移i位。

col[i]表示第j列在哪些行的值为1,哪些行的值为0。

dfs(int cur, int push, int pop)表示目前考查到了第cur列,push表示已经选出来的行,pop表示已经放弃的行。

if(ok) return 0;不要没错,但是不要浪费时间,如果说有x行都满足在第cur列的要求,第一行dfs已经使ok = 1了,若不要上述的这个判断,那么剩下的x-1行都来一次dfs,费时。

if(push&pop) return 0;前面放入push的行只是满足了当时的cur列要求,并不能保证现在的cur列是否满足要求。

dfs(cur + 1, push | row[i], pop | (row[i]^col[cur]));这里没有修改push和pop的值,所以回溯也不用修改push和pop的值。

好题,好题~

#include <cstdio>
#include <cstring>

using namespace std;

const int maxr = 16;
const int maxc = 300;
int M, N, row[maxr], col[maxc], MAP[maxr][maxc];
bool ok;

void dfs(int cur, int push, int pop)
{
    if(ok || (push&pop)) return;
    if(cur == N)
    {
        ok = 1;
        return;
    }
    for(int i = 0; i < M; i++)
    {
        int temp = row[i] & col[cur];
        if(temp && !(row[i]&pop))
        {
            dfs(cur + 1, push | row[i], pop | (row[i]^col[cur]));
        }
    }
}

int main()
{
    int i, j;
    for(i = 0; i < maxr; i++) row[i] = 1<<i;
    while(scanf("%d%d", &M, &N) == 2)
    {
        memset(col, 0, sizeof(col));
        for(i = 0; i < M; i++)
            for(j = 0; j < N; j++)
            {
                scanf("%d", &MAP[i][j]);
                if(MAP[i][j]) col[j] |= row[i];
            }
        ok = 0;
        dfs(0, 0, 0);
        if(ok)
            printf("Yes, I found it\n");
        else
            printf("It is impossible\n");
    }
    return 0;
}


你可能感兴趣的:(poj - 3740 - Easy Finding(精确覆盖DLX))