zoukankan      html  css  js  c++  java
  • dancing link

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

    链接给的博客写的很好,比较好懂。

    可惜不是c语言...

    于是决定自己要建一个模板。

     一道裸题:hustoj 1017 exact cover 输入一个矩阵求选哪些行,使得这是一个精确覆盖

    AC通道:http://acm.hust.edu.cn/problem/show/1017

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 
      5 using namespace std;
      6 
      7 const int maxn=1010;
      8 const int INF=0x7fffffff;
      9 
     10 typedef int array[maxn];
     11 typedef int Array[maxn*maxn];
     12 
     13 int n,m,cnt,size;
     14 array last,f,d,ans;
     15 Array L,R,U,D,c,r,s;
     16 
     17 void build(){    //初始化
     18     /*构造矩阵第一行:
     19     1.将第一行的两端环状处理。[提示]:第一行的元素的标号就是其列的标号。
     20     2.对于第一行的所有元素:
     21         连接其左右;标注其行列;清空列中表示的元素个数;清空表示列的上一个元素的d数组。*/
     22 
     23     L[0]=m,R[m]=0;
     24     for(int i=1;i<=m;i++){
     25         L[i]=i-1,R[i-1]=i;
     26         c[i]=i;r[i]=0;
     27         s[i]=0;
     28         d[i]=0;
     29     }
     30     
     31     //清空所有行上的表示上一个元素的last数组和表示首元素的f数组
     32     for(int i=1;i<=n;i++) last[i]=f[i]=0;
     33 }
     34 
     35 void del(int col){    //删列操作
     36     /*
     37         1.在第一行中删除这个列节点。
     38         2.找到每个在这条列上的节点i
     39             将节点i所在的行从列表中删除
     40     */
     41     
     42     L[R[col]]=L[col],R[L[col]]=R[col];    //在第一行中删除这个列节点。
     43     
     44     for(int i=D[col];i!=col;i=D[i])
     45         for(int j=R[i];j!=i;j=R[j])    //将节点i所在的行删除干净[因为是环状的,所以可以删掉这行上的所有节点]
     46             U[D[j]]=U[j],D[U[j]]=D[j],s[c[j]]--;    //这些节点都在列表中不再被查询得到。
     47 }
     48 
     49 void add(int col){    //恢复列操作
     50     /*
     51         1.在第一行中恢复这个列节点。
     52         2.找到每个在这条列上的节点i
     53             将节点i所在的行有顺序的从列表中恢复
     54     */
     55     
     56     R[L[col]]=col,L[R[col]]=col;    //因为删除时其实是间接删除[将其两边元素的指针改变],恢复只需要从它自己出发恢复两边即可。
     57     
     58     for(int i=U[col];i!=col;i=U[i])    
     59         for(int j=L[i];j!=i;j=L[j])    //同删除的顺序相反的添加回来[即后删去的先添加],才保证了顺序,不会错
     60             U[D[j]]=j,D[U[j]]=j,s[c[j]]++;    //同删除的感觉,通过行表找到这些丢失的点,然后从它自己来恢复列表中它们的位置。
     61 }
     62 
     63 bool search(int k){
     64     if(R[0]==0){    //第一行中的所有元素都被删除[即列都被标记,完成了精确覆盖]了
     65         printf("%d",k);
     66         for(int i=1;i<=k;i++)
     67             printf(" %d",r[ans[i]]);
     68         putchar('
    ');
     69         return true;
     70     }
     71     int Min=INF,C;
     72     for(int i=R[0];i;i=R[i])
     73         if(Min>s[i]) Min=s[i],C=i;    //优先选择列中元素少的列来处理
     74     del(C);
     75     for(int i=D[C];i!=C;i=D[i]){    //确定要删掉这列,但是要考虑删掉哪一行[当然是必须要与这一列相交的行]
     76         ans[k+1]=i;
     77         for(int j=R[i];j!=i/*环状链表的好处,可以循环寻找,找到所有元素*/;j=R[j]) del(c[j]);    //当确定删掉i行时,还要删掉所有与其相交的列
     78         if(search(k+1)) return true;
     79         for(int j=L[i];j!=i;j=L[j])    /*环状链表的另一个好处,可以让添加的顺序与删除的顺序相反,从而达到后删去的先添加的形式*/
     80             add(c[j]);    //回溯过程,补充回原来删去的列。
     81     }
     82     add(C);
     83     return false;
     84 }
     85 
     86 void link(int row/**/,int col/**/){
     87     size++;    //点的数目++
     88     s[col]++;    //所在列的元素个数++
     89     
     90     /*行操作:
     91         如果这一行之前没有元素了,这个元素作为本行的首元素,记录在f[row]中;并记录在表示上一个元素的last[row]中。
     92         若所在行之前还有元素,将这个元素与上一个元素连起来,更新last[];并将这个元素的右端与该行的首元素连起来,形成一个环状的结构。*/
     93     if(!last[row]) 
     94         last[row]=size,
     95         R[size]=size,L[size]=size,
     96         f[row]=size;
     97     else
     98         L[size]=last[row],R[last[row]]=size,
     99         last[row]=size,
    100         R[size]=f[row],L[f[row]]=size;
    101     
    102     /*列操作:
    103         【与行操作有所不同】:我们事先已经构造出了矩阵的第一行,不存在所谓首元素,因为首元素就是这一列的标号。
    104         如果这一列之前没有添加过元素,将当前元素与这一列的标号首尾相连,更新表示上一个元素的d[col]。
    105         若之前添加过元素,将这个元素与上一个元素连起来;并将这个元素充当此行的末尾,与这一列的标号相连构成一个环状结构;更新d[col]。*/
    106     if(!d[col])
    107         U[col]=size,D[size]=col,
    108         D[col]=size,U[size]=col,
    109         d[col]=size;
    110         
    111     else 
    112         U[col]=size,D[size]=col,
    113         U[size]=d[col],D[d[col]]=size,
    114         d[col]=size;
    115     
    116     c[size]=col,r[size]=row; //处理完与其它节点的关系后,再给自己打上行列的标号
    117 }
    118 
    119 int main(){
    120     int k,x;
    121 
    122     while(~scanf("%d%d",&n,&m)){
    123         build();
    124         size=m;    //前m个元素已经使用过了,不参与计数
    125         for(int i=1;i<=n;i++){
    126             scanf("%d",&k);
    127             while(k--)
    128                 scanf("%d",&x),link(i,x);
    129         }
    130         if(!search(0)) puts("NO");
    131     }
    132     
    133     return 0;
    134 }
    View Code
  • 相关阅读:
    2021,6,10 xjzx 模拟考试
    平衡树(二)——Treap
    AtCoder Beginner Contest 204 A-E简要题解
    POJ 2311 Cutting Game 题解
    Codeforces 990G GCD Counting 题解
    NOI2021 SDPTT D2T1 我已经完全理解了 DFS 序线段树 题解
    第三届山东省青少年创意编程与智能设计大赛总结
    Luogu P6042 「ACOI2020」学园祭 题解
    联合省选2021 游记
    Codeforces 1498E Two Houses 题解 —— 如何用结论吊打标算
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/5058624.html
Copyright © 2011-2022 走看看