DLX算法介绍

在介绍DLX算法前,先说下X算法,通过比较才 知道DLX算法的好。

X算法是一种常规的回溯算法,我们可以用一个递归过程求精确覆盖问题。

递归过程描述为:选择一个没有被删除的列,然后枚举该列为1的行,将该行作为解的一部分,删除所选择的行中为1的列(在删除列时,将对应的行也删除)。用伪代码表示如下,其中A表示覆盖时的矩阵

if A is empty, the problem is solved; terminate successfully.

Otherwise choose a column, c.

Choose a row r, such that A[r,c] = 1.

Include r in the partial solution.

For each j such that A[r, j] = 1,

        delete column j from matrix A;

        for each i such A[i,j] = 1,

              delete row i from matrix A.

Repeat this algorithm recursively on the reduced matrix A.

而DLX的妙处就是删除列,因为删除列不是一个简单的操作,还需要把它所覆盖的行也删除。DLX用到了双向循环十字链表,删除和恢复会比较高效。

删除操作主要为:
L[R[x]] = L[x];

R[L[x]] = R[x];

恢复操作为:

L[R[x]] = x;

R[L[x]] = x;

Dancing links的每个普通结点对应矩阵中的一个1,另外还有n+1个虚拟结点,其中每列上方都有一个虚拟结点,而所有虚拟结点的最前面有一个头结点h。每个结点有五个域:L,R,U,D,C,其中L表示结点的左结点,R表示结点的右结点,U表示结点的上边的结点,D表示结点的下边的结点,C表示结点所对应的列上的虚拟结点。用S[x]表示第x列上的结点个数。如下面所示:


DLX的算法用递归过程用search(k)描述如下,k初始化为0

if R[h] = h, print the current solution and return.

Otherwise choose a column object c

Cover column c

For each r = D[c], D[D[c]],...., while r <> c

        set Ok = r

        for each j = R[r], R[R[r]],..., while j <> r

               cover column j

        search(k + 1)

        for each j = L[r], L[L[r]],..., while j <> r

              uncover column j

uncover column c

在选择列时,选择列中元素最少的列,用伪代码描述为

c = R[h]

s = 无穷大

for each j = R[h], R[R[h]],..., while j <> h

       if S[j] < s then s = s[j], c = j

在覆盖列,需要把覆盖的行也删除,伪代码为

L[R[c]] = L[c], R[L[c]] = R[c]

for i = D[c], D[D[c]], ..., while i <> c

      for j = R[i], R[R[i]],..., while j <> i

             U[D[j]] = U[j]    D[U[j]] = D[j]  S[C[j]]--

在恢复列时,伪代码为

for i = U[c], U[U[c]],...,while i <> c

       for j = L[i], L[L[i]], ..., while j != i

             U[D[j]] = j  D[U[j]] = j   S[C[j]]++

L[R[c]] = c           R[L[c]] = c

DLX的模板代码如下:

class DLX
{
private :
    int n;
    int L[MAXN], R[MAXN], U[MAXN], D[MAXN];
    int col[MAXN], row[MAXN], S[MAXN];
    int ans[MAXN], ansd;
    int sz;

public:
    void init(int n)
    {
        this->n = n;

        for (int i = 0; i <= n; i++) {
            L[i] = i - 1;
            R[i] = i + 1;
            U[i] = i;
            D[i] = i;
        }
        L[0] = n;
        R[n] = 0;
        sz = n + 1;

        memset(S, 0x00, sizeof(S));
    }

    void addRow(int r, vector<int> &columns)
    {
        int first = sz;
        size_t size = columns.size();
        for (size_t i = 0; i < size; i++) {
            int c = columns[i];
            L[sz] = sz - 1;
            R[sz] = sz + 1;
            U[sz] = U[c];
            D[U[c]] = sz;
            D[sz] = c;
            U[c] = sz;
            row[sz] = r;
            col[sz] = c;
            S[c]++;
            sz++;
        }

        L[first] = sz - 1;
        R[sz - 1] = first;
    }

    bool solve(vector<int> &v)
    {
        if (!dfs(0)) return false;

        v.clear();
        for (int i = 0; i < ansd; i++) {
            v.push_back(ans[i]);
        }

        return true;
    }

#define FOR(i, A, s) for (int i = A[s]; i != s; i = A[i])

    void cover(int c)
    {
        L[R[c]] = L[c];
        R[L[c]] = R[c];

        FOR (i, D, c) {
            FOR (j, R, i) {
                S[col[j]]--;
                D[U[j]] = D[j];
                U[D[j]] = U[j];
            }
        }
    }

    void uncover(int c)
    {
        FOR (i, U, c) {
            FOR (j, L, i) {
                S[col[j]]++;
                D[U[j]] = j;
                U[D[j]] = j;
            }
        }

        R[L[c]] = c;
        L[R[c]] = c;
    }

    bool dfs(int dep)
    {
        if (R[0] == 0) {
            ansd = dep;
            return true;
        }

        int c = R[0];
        FOR (i, S, 0) {
            if (S[i] < S[c]) c = i;
        }

        cover(c);
        FOR (i, D, c) {
            ans[dep] = row[i];
            FOR (j, R, i) {
                cover(col[j]);
            }

            if (dfs(dep + 1)) return true;

            FOR (j, L, i) {
                uncover(col[j]);
            }
        }

        uncover(c);
        return false;
    }
};


  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kgduu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值