• 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
  • 相关阅读:
    Leetcode总结之Tree
    Leetcode总结之DFS
    Leetcode总结之Backtracking
    策略模式(设计模式_02)
    面向对象思想设计_计算器小程序(设计模式入门篇_01)
    Android GridView 滑动条设置一直显示状态
    dp暑假专题 训练记录
    dp入门 专题记录 2017-7-26
    dp问题 -挑战例题 2017-7-24
    贪心思维 专题记录 2017-7-21
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/5058624.html
Copyright © 2020-2023  润新知