Dancing Links to solve the exact coverage problem (C++ code)

Many problems (such as solving Sudoku, etc.) can be transformed into solving precise coverage problems. It would be great if there is a convenient code to solve this problem.
So the great predecessors invented the X algorithm, using the dancing link to solve this problem.
https://blog.csdn.net/whereisherofrom/article/details/79220897
https://www.cnblogs.com/grenet/p/3145800.html
These two blogs explain the algorithm ideas of X algorithm very well.
It's a pity that the two pieces of code, one is a language I don't know, and the other is C++ code but it is more engineering. It feels not very conducive to quickly understand and implement this algorithm, so here is a simple implementation code in C++.

The core idea of ​​the X algorithm is to use dancing link for brute force search and then backtracking.
The title corresponds to Luogu P4929

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int maxn = 555;
struct node{
    
    
    int x, y;
    int l, r, u, d;
}e[maxn*maxn];
int head, col[maxn], row[maxn], tot = 0, sz[maxn];
int a[maxn][maxn];
void add(int i, int j){
    
    
    ++tot;//e[tot]代表新结点
    sz[j]++;//第j列的点数+1
    e[tot] = (node){
    
    i, j, e[row[i]].l, row[i], e[col[j]].u, col[j]};//四个方向
    e[e[tot].u].d = tot;//tot上面的点 的 下面的点 变成tot
    e[e[tot].d].u = tot;//tot下面的点 的 上面的点 变成tot
    e[e[tot].l].r = tot;//tot左面的点 的 右面的点 变成tot
    e[e[tot].r].l = tot;//tot右面的点 的 左面的点 变成tot
    return;
}
void init(int n, int m){
    
    
    tot = 0;
    head = ++tot;
    e[head] = (node){
    
    0, 0, head, head, head, head};
    for(int i = 1; i <= n; ++i){
    
    
        row[i] = ++tot;
        e[tot] = (node){
    
    i, 0, tot, tot, e[head].u,head};
        e[e[tot].u].d = tot;
        e[e[tot].d].u = tot;
    }
    for(int i = 1; i <= m; ++i){
    
    
        col[i] = ++tot;
        e[tot] = (node){
    
    0,i,e[head].l, head, tot, tot};
        e[e[tot].l].r = tot;
        e[e[tot].r].l = tot;
    }
    fors(i, 1, n+1) fors(j, 1, m+1) if(a[i][j]) add(i, j);
}
int n, m;
void del_lr(int id){
    
    
    e[e[id].r].l = e[id].l;
    e[e[id].l].r = e[id].r;
}
void del_ud(int id){
    
    
    e[e[id].u].d = e[id].d;
    e[e[id].d].u = e[id].u;
}
void re_lr(int id){
    
    
    e[e[id].r].l = e[e[id].l].r = id;
}
void re_ud(int id){
    
    
    e[e[id].u].d = e[e[id].d].u = id;
}
void cover(int y){
    
    //删除第y列
    del_lr(col[y]);
    for(int cy = e[col[y]].d; cy != col[y]; cy = e[cy].d){
    
    //枚举这个列上的1
        for(int cx = e[cy].l; cx != cy; cx = e[cx].l){
    
    //枚举行上的1,删除上下关系
            del_ud(cx);
            sz[e[cx].y]--;//这一列1的个数减1
        }
    }
}
void re(int y){
    
    //恢复第y列
    re_lr(col[y]);
    for(int cy = e[col[y]].d; cy != col[y]; cy = e[cy].d){
    
    
        for(int cx = e[cy].l; cx != cy; cx = e[cx].l){
    
    
            re_ud(cx);
            sz[e[cx].y]++;
        }
    }
}
stack<int> ans;
bool dance(){
    
    
    if(e[head].l == head){
    
    //空矩阵
        return true;
    }
    int mi = 0x3f3f3f3f, cid = -1;
    for(int i = e[head].r; i != head; i = e[i].r){
    
    
        if(mi > sz[e[i].y]) mi = sz[e[i].y], cid = e[i].y;
    }//找一个列上的1个数最少的列
    cover(cid);
    for(int cy = e[col[cid]].d; cy != col[cid]; cy = e[cy].d){
    
    //枚举选哪行
        for(int cx = e[cy].l; cx != cy; cx = e[cx].l){
    
    
            if(e[cx].y == 0) continue;//是辅助数组row对应的结点,则不删除列
            cover(e[cx].y);
        }
        ans.push(e[cy].x);//行号压入栈
        if(dance()) return true;//子问题得到解决,返回true

        ans.pop();//这个行号不行,弹出
        for(int cx = e[cy].l; cx != cy; cx = e[cx].l){
    
    //恢复刚刚被删除的列
            if(e[cx].y == 0) continue;
            re(e[cx].y);
        }
    }
    re(cid);
    return false;
}
int main()
{
    
    
    cin>>n>>m;
    fors(i, 1, n+1) fors(j, 1, m+1) scanf("%d", &a[i][j]);
    init(n, m);
    if(dance()){
    
    
        while(ans.size()) printf("%d ", ans.top()), ans.pop();
    }else{
    
    
        cout<<"No Solution!"<<endl;
    }
}

Guess you like

Origin blog.csdn.net/qq_43202683/article/details/104713106