DLX learning notes

Posted by NINTHTJ on Thu, 27 Jan 2022 03:11:36 +0100

DLX

\(\ text{DLX} \), dance chain, which mainly solves the problems of accurate coverage and repeated coverage.

This paper only introduces the exact coverage problem for the time being.

I don't know when to solve the problem of repeated coverage.

1. Template questions

P4929 [template] dance chain (DLX)

Meaning:

Given a matrix of \ (N \) rows \ (M \) columns, each element in the matrix is either 1 or 0

You need to select several rows in the matrix, so that for each column \ (j \) of the matrix, there is only one row whose (j \) th element is \ (1 \).

General idea

This kind of problem is roughly summarized into several steps.

  1. Modeling and converting the problem into the template of the above question.

  2. Start writing the board.

The board is roughly divided into the following steps:

  1. The map of cross linked list will be built.

  2. Select a row and mark each column with one.

  3. Then mark the row with one in each column.

  4. Delete all marked and synthesize a new matrix.

  5. If the new matrix is not empty, repeat the above steps; If it is empty, judge whether it is all one before. If so, there is a solution, otherwise there is no solution.

It should be clear now.

#include <bits/stdc++.h>
using namespace std;

const int maxn = 6000;

int n , m , tot , cnt;
int l[maxn] , r[maxn] , d[maxn] , u[maxn];
int qx[maxn] , qy[maxn] , vy[maxn] , ans[maxn];

inline int read()
{
    int asd = 0 , qwe = 1; char zxc;
    while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
    while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
    return asd * qwe;
}

inline void init()
{
    for(int i = 0;i <= m;i++)
    {
        l[i] = i - 1 , r[i] = i + 1;
        u[i] = d[i] = i;
    }
    r[m] = 0 , l[0] = m;
    cnt = m + 1;
}

inline void add(int &ll , int &rr , int x , int y)
{
    qx[cnt] = x , qy[cnt] = y , vy[y]++;
    u[cnt] = y , d[cnt] = d[y] , u[d[y]] = cnt , d[y] = cnt;
    r[ll] = l[rr] = cnt , l[cnt] = ll , r[cnt] = rr;
    rr = cnt++; 
}

inline void remove(int p)
{
    l[r[p]] = l[p] , r[l[p]] = r[p];
    for(int i = d[p];i != p;i = d[i])
        for(int j = l[i];j != i;j = l[j])
        {
            vy[qy[j]]--;
            u[d[j]] = u[j] , d[u[j]] = d[j];
        }
    return;
}

inline void resume(int p)
{
    for(int i = u[p];i != p;i = u[i])
        for(int j = r[i];j != i;j = r[j])
        {
            vy[qy[j]]++;
            u[d[j]] = d[u[j]] = j;
        }
    l[r[p]] = r[l[p]] = p;
    return;
}

inline bool dfs()
{
    if(!r[0]) return true;
    int p = r[0];
    for(int i = r[0];i;i = r[i])
        if(vy[p] > vy[i]) p = i;
    remove(p);
    for(int i = d[p];i != p;i = d[i])
    {
        ans[++tot] = qx[i];
        for(int j = r[i];j != i;j = r[j]) remove(qy[j]);
        if(dfs()) return true;
        for(int j = l[i];j != i;j = l[j]) resume(qy[j]);
        tot--;
    }
    resume(p);
    return false;
}

int main()
{
    n = read() , m = read();
    init();
    for(int i = 1;i <= n;i++)
    {
        int ll = cnt , rr = cnt;
        for(int j = 1;j <= m;j++)
        {
            int x = read();
            if(x) add(ll , rr , i , j);
        }
    }
    if(dfs())
        for(int i = 1;i <= tot;i++) cout << ans[i] << " ";
    else
        cout << "No Solution!";
    return 0;
}

2. Sudoku

Being a lazy person is the last question.

SP13980 SUDOGOB - Sudoku goblin

An obvious \ (\ text{DLX} \) template question.

Here we focus on how to model.

modeling

First, consider the conditions that a Sudoku needs to meet.

  • Each line needs to be different.

  • Each column needs to be different.

  • Each grid needs to be different.

  • None of the 81 cells can be \ (0 \).

Pay attention to the last case, which is the most easily missed case.

According to the nature of \ (\ text{DLX} \), we might as well set each case to each row and each limit to each column to build the model.

Consider each case

Let \ (a_{i,j} \) be the number on row \ (I \) and column \ (j \).

  • When \ (a_{i,j} \) is \ (0 \).

Then all nine numbers in this position may be filled in, so they all need to be added.

  • When \ (a_{i,j} \) is not \ (0 \).

Then there is only one solution to this position.

Consider each limitation

For the previous case, we need to add points in different columns according to the restrictions.

Since there are four restrictions, we can add four points.

The code is roughly as follows:

add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 0 + (i - 1) * 9 + j);
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 1 + (i - 1) * 9 + k);
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 2 + (j - 1) * 9 + k);
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 3 + ((i - 1) / 3 * 3 + (j - 1) / 3) * 9 + k);

The rest can be written directly on the board.

Code

#include <bits/stdc++.h>
using namespace std;

const int maxn = 5000;

int n , m , t , sum , cnt , tot , a[15][15];
int l[maxn] , r[maxn] , u[maxn] , d[maxn] , h[maxn] , qx[maxn] , qy[maxn] , vy[maxn] , ans[maxn];


inline int read()
{
    int s = 0 , f = 1; char ch;
    while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
    while(isdigit(ch)) s = s * 10 + ch - '0' , ch = getchar();
    return s * f;
}

inline void init()
{
    memset(l , 0 , sizeof(l));
    memset(r , 0 , sizeof(r));
    memset(d , 0 , sizeof(d));
    memset(u , 0 , sizeof(u));
    memset(h , -1 , sizeof(h));
    for(int i = 0;i <= m;i++)
    {
        l[i] = i - 1 , r[i] = i + 1;
        d[i] = u[i] = i;
    }
    tot = 0 , cnt = m + 1 , l[0] = m , r[m] = 0;
    memset(vy , 0 , sizeof(vy));
    memset(qx , 0 , sizeof(qx));
    memset(qy , 0 , sizeof(qy));
    memset(ans , 0 , sizeof(ans));
}

inline void add(int x , int y)
{
    qx[cnt] = x , qy[cnt] = y , vy[y]++;
    u[cnt] = y , d[cnt] = d[y] , u[d[y]] = cnt , d[y] = cnt;
    if(h[x] < 0) h[x] = l[cnt] = r[cnt] = cnt;
    else r[cnt] = h[x] , l[cnt] = l[h[x]] , l[h[x]] = r[l[h[x]]] = cnt;
    cnt++;
}

inline void remove(int p)
{
    l[r[p]] = l[p] , r[l[p]] = r[p];
    for(int i = d[p];i != p;i = d[i])
        for(int j = r[i];j != i;j = r[j])
        {
            vy[qy[j]]--;
            u[d[j]] = u[j] , d[u[j]] = d[j];
        }
    return;
}

inline void resume(int p)
{
    for(int i = u[p];i != p;i = u[i])
        for(int j = l[i];j != i;j = l[j])
        {
            vy[qy[j]]++;
            u[d[j]] = d[u[j]] = j;
        }
    l[r[p]] = r[l[p]] = p;
    return;
}

inline void dfs()
{
    if(!r[0])
    {
        sum++;
        for(int i = 1;i <= tot;i++)
            a[(ans[i] - 1) / 81 + 1][(ans[i] - 1) / 9 % 9 + 1] = (ans[i] - 1) % 9 + 1;
    }
    int p = r[0];
    for(int i = r[0];i;i = r[i])
        if(vy[p] > vy[i]) p = i;
    remove(p);
    for(int i = d[p];i != p;i = d[i])
    {
        ans[++tot] = qx[i];
        for(int j = r[i];j != i;j = r[j]) remove(qy[j]);
        dfs();
        for(int j = l[i];j != i;j = l[j]) resume(qy[j]);
        tot--;
    }
    resume(p);
}

int main()
{
    // freopen("1.in" , "r" , stdin);
    t = read() , n = 729 , m = 324;
    while(t--)
    {
        sum = 0 , init();
        for(int i = 1;i <= 9;i++)
        {
            for(int j = 1;j <= 9;j++)
            {
                a[i][j] = read();
                for(int k = 1;k <= 9;k++)
                {
                    if(!a[i][j] || a[i][j] == k)
                    {
                        add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 0 + (i - 1) * 9 + j);
                        add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 1 + (i - 1) * 9 + k);
                        add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 2 + (j - 1) * 9 + k);
                        add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 3 + ((i - 1) / 3 * 3 + (j - 1) / 3) * 9 + k);
                    }
                }
            }
        }
        dfs();
        cout << sum << endl;
        if(sum == 1)
        {
            for(int i = 1;i <= 9;i++)
            {
                for(int j = 1;j <= 9;j++)
                    cout << a[i][j] << " ";
                cout << endl;
            }
        }
    }
    return 0;
}

Topics: linked list search engine