• AcWing 1067. 精确覆盖问题 DLX


    地址 https://www.acwing.com/problem/content/description/1069/

    给定一个 N×M 的数字矩阵 A,矩阵中的元素 Ai,j∈{0,1}。
    
    请问,你能否在矩阵中找到一个行的集合,使得这些行中,每一列都有且仅有一个数字 1。
    
    输入格式
    第一行包含两个整数 N 和 M。
    
    接下来 N 行,每行包含 M 个整数(01),表示完整的数字矩阵。
    
    输出格式
    如果能找到满足条件的行的集合,则在一行中依次输出这些行的编号(行编号 1∼N)。
    
    如果方案不唯一,则以任意顺序输出任意方案即可。
    
    否则,输出 No Solution!。
    
    数据范围
    1≤N,M≤500,
    数据保证矩阵中 1 的数量不超过 5000。
    
    输入样例1:
    3 3
    0 1 0
    0 0 1
    1 0 0
    输出样例1:
    1 2 3
    输入样例2:
    4 4
    0 0 0 1
    1 0 0 0
    1 1 0 1
    0 1 0 0
    输出样例2:
    No Solution!

    自写指针版本 DLX
    这里是自己整理重新画图的V2版本 DLX学习心得

     

    // DLX_2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include <iostream>
    #include <vector>
    #include <assert.h>
    
    using namespace std;
    
    const int N = 510;
    
    
    /*
    3 3
    0 1 0
    0 0 1
    1 0 0
    
    1 2 3
    
    4 4
    0 0 0 1
    1 0 0 0
    1 1 0 1
    0 1 0 0
    
    No Solution!
    */
    
    
    struct DLX_NODE {
        struct DLX_NODE* up;
        struct DLX_NODE* down;
        struct DLX_NODE* left;
        struct DLX_NODE* right;
        int row; int col;
        int count;
        int ele;
    
        DLX_NODE() {
            up = NULL; down = NULL; left = NULL; right = NULL;
            col = -1; row = -1; ele = -1; count = 0;
        }
    };
    
    struct DLX_NODE table[N][N];
    
    int n, m;
    
    void init()
    {
        for (int i = 0; i < n; i++) {
            table[i][0].down = &table[i + 1][0];
            table[i + 1][0].up = &table[i][0];
            table[i][0].row = i; table[i][0].col = 0;
            table[i + 1][0].row = i + 1; table[i + 1][0].col = 0;
        }
    
        for (int i = 0; i < m; i++) {
            table[0][i].right = &table[0][i + 1];
            table[0][i + 1].left = &table[0][i];
            table[0][i].row = 0; table[0][i].col = i;
            table[0][i+1].row = 0; table[0][i+1].col = i+1;
        }
    
    
    }
    
    void printDLX()
    {
        struct DLX_NODE* p = &table[0][0];
        cout << "=================================================>
    ";
        p = p->right;
        while (p != NULL) {
            struct DLX_NODE* pp = p;
            if (pp->count != 0) {
                cout << "colBase.count = " << pp->count << endl;
            }
            while (pp != NULL) {
                if (pp->ele == 1)
                    cout << "pp->col = " << pp->col << ". pp->row = " << pp->row << ".pp->ele = " << pp->ele << endl;
                pp = pp->down;
            }
            p = p->right;
        }
        cout << endl << endl;
        p = &table[0][0];
        p = p->down;
        while (p != NULL) {
            struct DLX_NODE* pp = p;
            while (pp != NULL) {
                if (pp->ele == 1)
                    cout << "pp->col = " << pp->col << ". pp>row = " << pp->row << ".pp->ele = " << pp->ele << endl;
                pp = pp->right;
            }
            p = p->down;
        }
        cout << endl ;
    }
    
    
    
    void FindColInsert(int row,int col)
    {
        struct DLX_NODE* p = &table[0][col];
        while (p != NULL) {
            struct DLX_NODE* next = p->down;
            //找到链表结尾或者 next比当前元素行数更大 P的下面即是插入的位置
            if (next == NULL)  break;
            else if (next->row > row) break;
            else if (next->row == row) {
                //行列都相同则说明重复插入了
                assert(0);
            }
            p = next;
        }
        assert(p != NULL);
        //插入
        struct DLX_NODE* node = &table[row][col];
        node->down = p->down;
        if (p->down != NULL) p->down->up = node;
    
        node->up = p;
        p->down = node;
    
        table[0][col].count++;
    }
    
    
    void FindRowInsert(int row, int col)
    {
        struct DLX_NODE* p = &table[row][0];
        while (p != NULL) {
            struct DLX_NODE* next = p->right;
            //找到链表结尾或者 next比当前元素列数更大 P的右面即是插入的位置
            if (next == NULL)  break;
            else if (next->col > col) break;
            else if (next->col == col) {
                //行列都相同则说明重复插入了
                assert(0);
            }
            p = next;
        }
        assert(p != NULL);
        //插入
        struct DLX_NODE* node = &table[row][col];
        node->right = p->right;
        if (p->right != NULL) p->right->left = node;
    
        node->left = p;
        p->right = node;
    }
    
    void addNode(int num, int x, int y)
    {
        table[x][y].row = x;
        table[x][y].col = y;
        table[x][y].ele = num;
    
        //插入列中
        FindColInsert(x,y);
    
        //插入行中
        FindRowInsert(x,y);
    }
    
    void resume(struct DLX_NODE* pToResume)
    {
        struct DLX_NODE* pdown = pToResume->down;
        while (pdown != NULL) {
            int row = pdown->row;
            assert(row != -1);
            struct DLX_NODE* nextResume = &table[row][0];
            while (nextResume != NULL) {
                if (nextResume->col > 0)
                    table[0][nextResume->col].count++;
                nextResume->up->down = nextResume;
                if (nextResume->down != NULL) nextResume->down->up = nextResume;
                nextResume = nextResume->right;
            }
            pdown = pdown->down;
        }
    
    
        if (pToResume->right != NULL) pToResume->right->left = pToResume;
        pToResume->left->right = pToResume;
    
    
    }
    
    void remove(struct DLX_NODE* pToRemove)
    {
        if (pToRemove->right != NULL) { pToRemove->right->left = pToRemove->left; }
        pToRemove->left->right = pToRemove->right;
    
        struct DLX_NODE* pdown = pToRemove->down;
        while (pdown != NULL) {
            int row = pdown->row;
            assert(row != -1);
            struct DLX_NODE* nextRemove = &table[row][0];
            while (nextRemove != NULL) {
                if(nextRemove->col != 0)
                    table[0][nextRemove->col].count--;
                assert(table[0][nextRemove->col].count >= 0);
                if (nextRemove->col == pToRemove->col) {
                    nextRemove = nextRemove->right;
                    continue;
                }
                if (nextRemove->down != NULL) nextRemove->down->up = nextRemove->up;
                nextRemove->up->down = nextRemove->down;
                nextRemove = nextRemove->right;
            }
            pdown = pdown->down;
        }
    }
    
    vector<int> ans;
    bool dfs()
    {
        int noEle = 1;  int minCount = 99999; struct DLX_NODE* selectP = NULL;
        struct DLX_NODE* p = table[0][0].right;
        if (p == NULL) 
            return true;
        while (p != NULL) {
            if (p->count != -1 && p->count < minCount) {
                minCount = p->count;
                selectP = p;
            }
            p = p->right;
        }
    
    
        remove(selectP);
        {
            p = selectP->down;
            while (p != NULL) {
                ans.push_back(p->row);
                struct DLX_NODE* tmp = table[p->row][0].right;
                while (tmp != NULL) {
                    if(tmp->col != selectP->col)
                        remove(&table[0][tmp->col]);
                    tmp = tmp->right;
                }
                if (dfs()) return true;
    
                tmp = table[p->row][0].right;
                while (tmp != NULL) {
                    if (tmp->col != selectP->col)
                        resume(&table[0][tmp->col]);
                    tmp = tmp->right;
                }
                ans.pop_back();
                p = p->down;
            }
        }
    
        resume(selectP);
    
        return false;
    }
    
    
    
    int main()
    {
        cin >> n >> m;
        init();
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                int x; cin >> x;
                if (x) {
                    addNode(x, i, j);
                }
            }
        }
        //printDLX();
        if (dfs())
        {
            for(auto& e:ans)  
                printf("%d ", e);
            puts("");
        }
        else puts("No Solution!");
    
    
    
        return 0;
    }
    
    
    作者:itdef
    链接:https://www.acwing.com/solution/content/26693/
    来源:AcWing
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    View Code

    实际使用建议使用Y总模板
    使用了链式前向星的十字链表DLX

     1 #include <iostream>
     2 #include <cstring>
     3 #include <algorithm>
     4 
     5 using namespace std;
     6 
     7 const int N = 5510;
     8 
     9 int n, m;
    10 int l[N], r[N], u[N], d[N], s[N], row[N], col[N], idx;
    11 int ans[N], top;
    12 
    13 void init()
    14 {
    15     for (int i = 0; i <= m; i ++ )
    16     {
    17         l[i] = i - 1, r[i] = i + 1;
    18         u[i] = d[i] = i;
    19     }
    20     l[0] = m, r[m] = 0;
    21     idx = m + 1;
    22 }
    23 
    24 void add(int& hh, int& tt, int x, int y)
    25 {
    26     row[idx] = x, col[idx] = y, s[y] ++ ;
    27     u[idx] = y, d[idx] = d[y], u[d[y]] = idx, d[y] = idx;
    28     r[hh] = l[tt] = idx, r[idx] = tt, l[idx] = hh;
    29     tt = idx ++ ;
    30 }
    31 
    32 void remove(int p)
    33 {
    34     r[l[p]] = r[p], l[r[p]] = l[p];
    35     for (int i = d[p]; i != p; i = d[i])
    36         for (int j = r[i]; j != i; j = r[j])
    37         {
    38             s[col[j]] -- ;
    39             u[d[j]] = u[j], d[u[j]] = d[j];
    40         }
    41 }
    42 
    43 void resume(int p)
    44 {
    45     for (int i = u[p]; i != p; i = u[i])
    46         for (int j = l[i]; j != i; j = l[j])
    47         {
    48             u[d[j]] = j, d[u[j]] = j;
    49             s[col[j]] ++ ;
    50         }
    51     r[l[p]] = p, l[r[p]] = p;
    52 }
    53 
    54 bool dfs()
    55 {
    56     if (!r[0]) return true;
    57     int p = r[0];
    58     for (int i = r[0]; i; i = r[i])
    59         if (s[i] < s[p])
    60             p = i;
    61     remove(p);
    62     for (int i = d[p]; i != p; i = d[i])
    63     {
    64         ans[ ++ top] = row[i];
    65         for (int j = r[i]; j != i; j = r[j]) remove(col[j]);
    66         if (dfs()) return true;
    67         for (int j = l[i]; j != i; j = l[j]) resume(col[j]);
    68         top -- ;
    69     }
    70     resume(p);
    71     return false;
    72 }
    73 
    74 int main()
    75 {
    76     scanf("%d%d", &n, &m);
    77     init();
    78     for (int i = 1; i <= n; i ++ )
    79     {
    80         int hh = idx, tt = idx;
    81         for (int j = 1; j <= m; j ++ )
    82         {
    83             int x;
    84             scanf("%d", &x);
    85             if (x) add(hh, tt, i, j);
    86         }
    87     }
    88 
    89     if (dfs())
    90     {
    91         for (int i = 1; i <= top; i ++ ) printf("%d ", ans[i]);
    92         puts("");
    93     }
    94     else puts("No Solution!");
    95 
    96     return 0;
    97 }
    View Code

    //====================================================

    //数独的DLX解答代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 20000;
    
    int m = 9 * 9 * 4;
    int u[N], d[N], l[N], r[N], s[N], col[N], row[N], idx;
    int ans[N], top;
    struct Op
    {
        int x, y;
        char z;
    }op[N];
    char g[20][9];
    char input[90];
    
    void init()
    {
        for (int i = 0; i <= m; i++)
        {
            l[i] = i - 1, r[i] = i + 1;
            s[i] = 0;
            d[i] = u[i] = i;
        }
        l[0] = m, r[m] = 0;
        idx = m + 1;
    }
    
    void add(int& hh, int& tt, int x, int y)
    {
        row[idx] = x, col[idx] = y, s[y] ++;
        u[idx] = y, d[idx] = d[y], u[d[y]] = idx, d[y] = idx;
        r[hh] = l[tt] = idx, r[idx] = tt, l[idx] = hh;
        tt = idx++;
    }
    
    void remove(int p)
    {
        r[l[p]] = r[p], l[r[p]] = l[p];
        for (int i = d[p]; i != p; i = d[i])
            for (int j = r[i]; j != i; j = r[j])
            {
                s[col[j]] --;
                u[d[j]] = u[j], d[u[j]] = d[j];
            }
    }
    
    void resume(int p)
    {
        for (int i = u[p]; i != p; i = u[i])
            for (int j = l[i]; j != i; j = l[j])
            {
                u[d[j]] = j, d[u[j]] = j;
                s[col[j]] ++;
            }
        r[l[p]] = p, l[r[p]] = p;
    }
    
    bool dfs()
    {
        if (!r[0]) return true;
        int p = r[0];
        for (int i = r[0]; i; i = r[i])
            if (s[i] < s[p])
                p = i;
        remove(p);
        for (int i = d[p]; i != p; i = d[i])
        {
            ans[++top] = row[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]) resume(col[j]);
            top--;
        }
        resume(p);
        return false;
    }
    
    int main()
    {
        while (~scanf("%s", input))
        {
            if (input[0] == 'e') break;
    
            memset(g, 0, sizeof(g[0][0]) * 20 * 9);
            int ii = 0;
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    g[i][j] = input[ii++];
                }
            }
    
            init();
    
            for (int i = 0, n = 1; i < 9; i++)
                for (int j = 0; j < 9; j++)
                {
                    int a = 0, b = 8;
                    if (g[i][j] != '.') a = b = g[i][j] - '1';
                    for (int k = a; k <= b; k++, n++)
                    {
                        int hh = idx, tt = idx;
                        op[n] = { i, j, (char)(k + '1') };
                        add(hh, tt, n, i * 9 + j + 1);
                        add(hh, tt, n, 81 + i * 9 + k + 1);
                        add(hh, tt, n, 81 * 2 + j * 9 + k + 1);
                        add(hh, tt, n, 81 * 3 + (i / 3 * 3 + j / 3) * 9 + k + 1);
                    }
                }
    
            dfs();
            for (int i = 1; i <= top; i++)
            {
                auto t = op[ans[i]];
                g[t.x][t.y] = t.z;
            }
    
    
            puts(g[0]);
            //puts("");
        }
    
        return 0;
    }
     

    参考链接
    https://www.cnblogs.com/ivan-count/p/7355431.html

    https://www.cnblogs.com/grenet/p/3145800.html

    https://blog.csdn.net/whereisherofrom/article/details/79220897

    https://www.luogu.org/blog/ONE-PIECE/qian-tan-dlx 【模板】舞蹈链(DLX)

    作 者: itdef
    欢迎转帖 请保持文本完整并注明出处
    技术博客 http://www.cnblogs.com/itdef/
    B站算法视频题解
    https://space.bilibili.com/18508846
    qq 151435887
    gitee https://gitee.com/def/
    欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
    如果觉得不错,欢迎点赞,你的鼓励就是我的动力
    阿里打赏 微信打赏
  • 相关阅读:
    没有精神分裂的测试不是一个好家长
    防火墙中配置开放 8080端口--续上一篇
    rocketMQ(一)基础环境
    如何做一个对账系统
    通用对账系统介绍与设计(上)
    pdf转图片
    虚拟机加载类机制
    jenkins
    zookeeper和dubbo
    正则日常积累
  • 原文地址:https://www.cnblogs.com/itdef/p/14108020.html
Copyright © 2020-2023  润新知