在介绍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;
}
};