zoukankan      html  css  js  c++  java
  • Dancing links

     

    基本思路(Main Thoughts):

         Dancing link是一种十分优美的数据结构。

         通常配合IDA*,二分等方法解决可以转化为精确覆盖和重复覆盖的题目。

         精确覆盖:在一个01矩阵中选几行,使得这几行组合起来的矩阵每列有且只有一个1

         重复覆盖:每列可以有多个1


     

    实现步骤(Implementation Steps):

         如果是正常的搜索

         解决步骤:

    1. 选最左边第一个没有被删除的列
    2. 选择一个在1中选出的列中有1的行,其他行直接删除
    3. 删掉2中选出的行剩余的元素所在的列
    4. 重复1,直到有一次没有可选列
    5. 如果一列没有被删除且列中已经没有元素,则回溯重新选择一行

      DLX基本上用到的就是这种思想

         辅助数组:

             R,L,U,D代表当前元素在链表中右左上下的元素

             C代表i点所在列

        S代表i列元素个数

             F,last,Last用于建表,分别是i行第一个元素,i行上一个元素,i列上一个元素。

        

      执行步骤:

        1、Dancing函数的入口

        2、判断Head.Right=Head?,若是,输出答案,返回True,退出函数。

        3、获得Head.Right的元素C

        4、标示元素C

        5、获得元素C所在列的一个元素

        6、标示该元素同行的其余元素所在的列首元素

        7、获得一个简化的问题,递归调用Daning函数,若返回的True,则返回True,退出函数。

        8、若返回的是False,则回标该元素同行的其余元素所在的列首元素,回标的顺序和之前标示的顺序相反

        9、获得元素C所在列的下一个元素,若有,跳转到步骤6

        10、若没有,回标元素C,返回False,退出函数。

      由于篇幅有限,在此贴出大神blog : http://www.cnblogs.com/grenet/p/3145800.html

      以供大家参考。

     

    模板(Code):

    //感谢诚叙同学的修改和注释 http://www.cnblogs.com/Robert-Yuan/

      1 void build(){    //初始化
      2     /*构造矩阵第一行:
      3     1.将第一行的两端环状处理。[提示]:第一行的元素的标号就是其列的标号。
      4     2.对于第一行的所有元素:
      5         连接其左右;标注其行列;清空列中表示的元素个数;清空表示列的上一个元素的d数组。*/
      6 
      7     L[0]=m,R[m]=0;
      8     for(int i=1;i<=m;i++){
      9         L[i]=i-1,R[i-1]=i;
     10         c[i]=i;r[i]=0;
     11         s[i]=0;
     12         d[i]=0;
     13     }
     14     
     15     //清空所有行上的表示上一个元素的last数组和表示首元素的f数组
     16     for(int i=1;i<=n;i++) last[i]=f[i]=0;
     17 }
     18 
     19 void del(int col){    //删列操作
     20     /*
     21         1.在第一行中删除这个列节点。
     22         2.找到每个在这条列上的节点i
     23             将节点i所在的行从列表中删除
     24     */
     25     
     26     L[R[col]]=L[col],R[L[col]]=R[col];    //在第一行中删除这个列节点。
     27     
     28     for(int i=D[col];i!=col;i=D[i])
     29         for(int j=R[i];j!=i;j=R[j])    //将节点i所在的行删除干净[因为是环状的,所以可以删掉这行上的所有节点]
     30             U[D[j]]=U[j],D[U[j]]=D[j],s[c[j]]--;    //这些节点都在列表中不再被查询得到。
     31 }
     32 
     33 void add(int col){    //恢复列操作
     34     /*
     35         1.在第一行中恢复这个列节点。
     36         2.找到每个在这条列上的节点i
     37             将节点i所在的行有顺序的从列表中恢复
     38     */
     39     
     40     R[L[col]]=col,L[R[col]]=col;    //因为删除时其实是间接删除[将其两边元素的指针改变],恢复只需要从它自己出发恢复两边即可。
     41     
     42     for(int i=U[col];i!=col;i=U[i])    
     43         for(int j=L[i];j!=i;j=L[j])    //同删除的顺序相反的添加回来[即后删去的先添加],才保证了顺序,不会错
     44             U[D[j]]=j,D[U[j]]=j,s[c[j]]++;    //同删除的感觉,通过行表找到这些丢失的点,然后从它自己来恢复列表中它们的位置。
     45 }
     46 
     47 bool search(int k){
     48     if(R[0]==0){    //第一行中的所有元素都被删除[即列都被标记,完成了精确覆盖]了
     49         printf("%d",k);
     50         for(int i=1;i<=k;i++)
     51             printf(" %d",r[ans[i]]);
     52         putchar('
    ');
     53         return true;
     54     }
     55     int Min=INF,C;
     56     for(int i=R[0];i;i=R[i])
     57         if(Min>s[i]) Min=s[i],C=i;    //优先选择列中元素少的列来处理
     58     del(C);
     59     for(int i=D[C];i!=C;i=D[i]){    //确定要删掉这列,但是要考虑删掉哪一行[当然是必须要与这一列相交的行]
     60         ans[k+1]=i;
     61         for(int j=R[i];j!=i/*环状链表的好处,可以循环寻找,找到所有元素*/;j=R[j]) del(c[j]);    //当确定删掉i行时,还要删掉所有与其相交的列
     62         if(search(k+1)) return true;
     63         for(int j=L[i];j!=i;j=L[j])    /*环状链表的另一个好处,可以让添加的顺序与删除的顺序相反,从而达到后删去的先添加的形式*/
     64             add(c[j]);    //回溯过程,补充回原来删去的列。
     65     }
     66     add(C);
     67     return false;
     68 }
     69 
     70 void link(int row/**/,int col/**/){
     71     size++;    //点的数目++
     72     s[col]++;    //所在列的元素个数++
     73     
     74     /*行操作:
     75         如果这一行之前没有元素了,这个元素作为本行的首元素,记录在f[row]中;并记录在表示上一个元素的last[row]中。
     76         若所在行之前还有元素,将这个元素与上一个元素连起来,更新last[];并将这个元素的右端与该行的首元素连起来,形成一个环状的结构。*/
     77     if(!last[row]) 
     78         last[row]=size,
     79         R[size]=size,L[size]=size,
     80         f[row]=size;
     81     else
     82         L[size]=last[row],R[last[row]]=size,
     83         last[row]=size,
     84         R[size]=f[row],L[f[row]]=size;
     85     
     86     /*列操作:
     87         【与行操作有所不同】:我们事先已经构造出了矩阵的第一行,不存在所谓首元素,因为首元素就是这一列的标号。
     88         如果这一列之前没有添加过元素,将当前元素与这一列的标号首尾相连,更新表示上一个元素的d[col]。
     89         若之前添加过元素,将这个元素与上一个元素连起来;并将这个元素充当此行的末尾,与这一列的标号相连构成一个环状结构;更新d[col]。*/
     90     if(!d[col])
     91         U[col]=size,D[size]=col,
     92         D[col]=size,U[size]=col,
     93         d[col]=size;
     94         
     95     else 
     96         U[col]=size,D[size]=col,
     97         U[size]=d[col],D[d[col]]=size,
     98         d[col]=size;
     99     
    100     c[size]=col,r[size]=row; //处理完与其它节点的关系后,再给自己打上行列的标号
    101 }
    View Code

    时间&空间复杂度(Time & Memory Complexity):

         空间:O(?)

         时间:O(?)

    主要用途&优缺点(Main Applications & Advantages & Disadvantages):

      主要用途: 通常配合IDA*,二分等方法解决可以转化为精确覆盖和重复覆盖的题目。

           优点:快

           缺点:长

    推荐题目&数据(Recommendatory Problems & Data) :

         Hustoj 1017 裸的精确覆盖

         UVA 1603 破坏正方形 DLX重复覆盖

     

     

  • 相关阅读:
    iap 详细
    血的教训,下次开工程 一点要写好判断空字符串方法
    iOS中的ScrollView
    自定义弹框加载方式
    CAGradientLayer简介(处理视图渐变色)
    iOS 制作view渐变的效果CAGradientLayer
    将vs2012的项目转化成VS2010
    关于Excel导入的HDR=YES; IMEX=1详解
    C#读取Excel表中的数据时,为何有些行的字段内容读取不到
    OLEDB读取EXCEL表格时,某些字段为空,怎么办?
  • 原文地址:https://www.cnblogs.com/tuigou/p/5052570.html
Copyright © 2011-2022 走看看