HUSTOJ1017_Exact cover _跳舞链DLX的精确覆盖

来源:互联网 发布:英制螺纹大小径算法 编辑:程序博客网 时间:2024/05/14 17:05

题意

给一个 n * m 的图,每一个格子上写着 1 或 0。要求选出一个行的集合,由这个行集合构成的子图中,每一列有且只有一个 1。输出集合元素个数并输出集合,或输出NO。

思路

第一个DLX,学到了。
这类问题的学名叫精确覆盖问题。DLX准确地讲是一种数据结构,可以高效地进行矩阵的递归和回溯。
DLX详解:http://www.cnblogs.com/grenet/p/3145800.html
kuangbin的模板:http://www.cnblogs.com/kuangbin/p/3752854.html

题目链接

http://acm.hust.edu.cn/problem/show/1017

AC代码

可以说是默写了一遍 kuangbin 的模板

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn = 1010;                                              //最大行数 const int maxm = 1010;                                              //最大列数 const int maxv = maxn * 100;                                        //最大节点数 //Dancing Links 类 struct DLX{    int n, m, size;                                                 //行数、列数、节点数     int U[maxv], D[maxv], L[maxv], R[maxv], Row[maxv], Col[maxv];   //上、下、左、右指针,行、列号     int S[maxm], H[maxn];                                           //列的节点个数,行的头指针     int ansd, ans[maxn];                                            //答案栈     void init(int _n, int _m)                                       //初始化函数     {        n = _n, m = _m;        for(int j= 0; j<= m; j++)        {            Row[j] = 0, Col[j] = j;                                 //行、列号             U[j] = D[j] = j;                                        //上下指针             S[j] = 0;            L[j] = j - 1, R[j] = j + 1;                             //左右指针        }        L[0] = m, R[m] = 0;                                         //端点左右指针         size = m;                                                   //节点个数(初始 = 列数)(附加节点)         for(int i= 1; i<= n; i++)                                   //行的头指针             H[i] = -1;    }    void link(int r, int c)                                         //向链表中插入节点     {        size ++;                                                    //节点数目         Row[size] = r, Col[size] = c;                               //行号、列号         S[c] ++;                                                    //插入列链表         D[size] = D[c];        U[D[c]] = size;        D[c] = size;        U[size] = c;        if(H[r] == -1) H[r] = L[size] = R[size] = size;             //将节点插入行链表         else{                                                       //第一个节点之后             R[size] = R[H[r]];            L[R[H[r]]] = size;            L[size] = H[r];            R[H[r]] = size;        }    }    void remove(int c)                                              //删除列函数(同时删除列中节点所在的行)     {        L[R[c]] = L[c], R[L[c]] = R[c];                             //列删除         for(int i= D[c]; i!= c; i= D[i])                            //删除列中节点所在的行             for(int j= R[i]; j!= i; j= R[j])            {                U[D[j]] = U[j], D[U[j]] = D[j];                S[Col[j]] --;            }    }    void resume(int c)                                              //恢复列函数(同时恢复列中节点所在的行     {        R[L[c]] = L[R[c]] = c;                                      //列恢复         for(int i= U[c]; i!= c; i= U[i])                            //恢复列中节点所在的行             for(int j= L[i]; j!= i; j= L[j])            {                U[D[j]] = j, D[U[j]] = j;                S[Col[j]] ++;            }    }    bool Dance(int d)                                               //搜索函数     {           if(R[0] == 0)                                               //剩余列为空,搜索成功         {            ansd = d;            return true;        }        int c = R[0];                                               //从节点少的列开始搜索,可以减少状态数         for(int j= R[0]; j!= 0; j= R[j])            if(S[j] < S[c]) c = j;        remove(c);                                                  //删除当前列         for(int i= D[c]; i!= c; i= D[i])                            //遍历选取当前列中节点所在的行         {            ans[d] = Row[i];                                        //行号压入答案栈             for(int j= R[i]; j!= i; j= R[j]) remove(Col[j]);        //删除行中节点对应的列             if(Dance(d+1)) return true;                             //向下一层搜索             for(int j= L[i]; j!= i; j= L[j]) resume(Col[j]);        //搜索失败的话还原当前行         }        resume(c);         return false;    }};int n, m;DLX g;int main(){    while(cin >> n >> m)    {        g.init(n, m);                           //初始化         for(int i= 1; i<= n; i++)               //构造链表         {            int num;            cin >> num;            while(num --)            {                int j;                cin >> j;                g.link(i, j);            }        }        if(g.Dance(0)){                         //DLX             printf("%d ", g.ansd);            for(int i= 0; i< g.ansd; i++)                printf("%d ", g.ans[i]);            printf("\n");         }        else printf("NO\n");    }    return 0;}
原创粉丝点击