在学习DancingLinks之前,我们先来回顾一下我们以前学过的回溯法。

我们学习基础的回溯法的时候,我们都是先判断是否达到解,然后继续搜索。

对于搜到的下一个点,将他标记为使用过( vis[i]=1; ),然后进入下一层搜索。

当解决精确覆盖问题(给定几个集合,使得找出其中一个或几个集合,满足这些集合中的元素互不重复,然后覆盖$[1,n]$的每一个数)的时候,我们发现普通的回溯算法不好写,而且我们需要模拟一个01矩阵。例如下面这个矩阵,他表示有四个集合$S_1,S_2,S_3,S_4$,其中有$3$列,当第$i$行第$j$列为1时,表示集合$S_i$中有元素$j$。我们要求的精准覆盖,就是找出几个集合,满足他们交集为空,并集刚好覆盖每一列。例如下面的$S_1$和$S_3$。(刚好覆盖3列,且没有重复)

$$\begin{pmatrix} 1 & 0 & 1 \\ 0 & 1 & 1 \\  0 & 1 & 0 \\ 1 & 1 & 0 \end{pmatrix}$$

暴力搜索需要$O(2^n \times m)$的时间复杂度。然后我们需要一个复杂度相对优秀的数据结构帮助我们写回溯算法。于是Donald E.Knuth发明了舞蹈链。这个数据结构在缓存和回溯的过程中效率惊人,不需要额外的空间,以及近乎线性的时间。而在整个求解过程中,指针在数据之间跳跃着,就像精巧设计的舞蹈一样,由此得名。

Dancing Links用的数据结构是交叉十字循环双向链。

因为精确覆盖问题所模拟的矩阵往往是稀疏矩阵(矩阵中,0的个数多于1),Dancing Links仅仅记录矩阵中值是1的元素。

然后在回溯算法中,我们就把标记为已用改成删除这一列。

然后按照dfs的模板打一下。

那么怎么实现插入和删除呢?我们考虑普通的链表,它的插入和删除就是找到一个节点,然后把它前面和后面的连起来(删除)或者分别连接前一个和后一个(插入)。舞蹈链也类似,当搜索到有一个集合是就是删除集合中所有为1的列。

于是我们整理出了一个回溯的过程(X算法)。

1、从矩阵中选择一行

2、根据定义,标示矩阵中其他行的元素

3、删除相关行和列的元素,得到新矩阵

4、如果新矩阵是空矩阵,并且之前的一行都是1,那么求解结束,跳转到6;新矩阵不是空矩阵,继续求解,跳转到1;新矩阵是空矩阵,之前的一行中有0,跳转到5

5、说明之前的选择有误,回溯到之前的一个矩阵,跳转到1;如果没有矩阵可以回溯,说明该问题无解,跳转到7

6、求解结束,把结果输出

7、求解结束,输出无解消息

因为我们使用了DancingLinks实现X算法,所以这个算法又叫DLX算法。

具体实现上,我们对于每一个结点记下它的左边一列(lt)右边一列(rt)上面一行(up)下面一行(dn),然后注意初始时按照输入插入( insert(r,c) 表示集合$S_r$有元素$c$),回溯时除了删除,还有恢复(就像写普通搜索时vis要恢复为0一样)。恢复刚好与删除相反。

那么有同学就会提出疑问:是不是会出现删除之后还没恢复就在同一层继续求解呢(这样会导致答案错误)?答案是不会。因为我们的dance()函数是按照dfs顺序,当没有恢复的时候不会再同一层再往下搜(即先删除先恢复的性质)。

那么这样我们就把DancingLinks的基础知识点就讲完了。

附:Cpp代码(恢复代码中有关tot_ans和ans[]的内容,最后可以输出覆盖方案)。

struct DancingLinks
{//initstatic const int MAXN=1010,MAXM=1010,MAXV=(1000000>>3)+10;int n,m,sz;int up[MAXV],dn[MAXV],lt[MAXV],rt[MAXV],row[MAXV],col[MAXV];int ph[MAXN],ps[MAXM];//记录行的选择情况和列的覆盖情况
//    int tot_ans,ans[MAXN];void init(int _n,int _m){n=_n;m=_m;sz=m;for(int i=0;i<=m;i++){ps[i]=0;up[i]=dn[i]=i;lt[i]=i-1;rt[i]=i+1;}rt[m]=0;lt[0]=m;for(int i=1;i<=n;i++)ph[i]=-1;}//operationvoid insert(int r,int c){ps[c]++;col[++sz]=c;row[sz]=r;up[sz]=c;up[dn[c]]=sz;dn[sz]=dn[c];dn[c]=sz;if(ph[r]<0)//headph[r]=lt[sz]=rt[sz]=sz;  else{lt[sz]=ph[r];rt[sz]=rt[ph[r]];lt[rt[ph[r]]]=sz;rt[ph[r]]=sz;}return;}void remove(int c){lt[rt[c]]=lt[c];rt[lt[c]]=rt[c];for(int i=dn[c];i!=c;i=dn[i])for(int j=rt[i];j!=i;j=rt[j]){up[dn[j]]=up[j];  dn[up[j]]=dn[j];  ps[col[j]]--;  }}void rebuild(int c){for(int i=up[c];i!=c;i=up[i])for(int j=lt[i];j!=i;j=lt[j]){up[dn[j]]=dn[up[j]]=j;ps[col[j]]++;  }lt[rt[c]]=rt[lt[c]]=c;}//dancebool dance(int d){if(rt[0]==0){
//            tot_ans=d;return 1;}int c=rt[0];  for(int i=rt[0];i;i=rt[i])if(ps[i]<ps[c])  c=i;remove(c);for(int i=dn[c];i!=c;i=dn[i])  {
//            ans[d]=row[i];for(int j=rt[i];j!=i;j=rt[j])remove(col[j]);if(dance(d+1))return 1;for(int j=lt[i];j!=i;j=lt[j])  rebuild(col[j]);  }  rebuild(c);return 0;}
}dlx;DancingLinks

参考资料:跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题

数据结构4——浅谈DancingLinks的思想及应用相关推荐

  1. 浅谈网络营销思想的演变与网络营销策略的升级

    许多企业在网站建设完成之后,经过常规的网站推广措施,没有取得明显效果,然后就不知道下一步的网络营销应该如何进行了,于是只能停留在网站建设和网站推广的阶段.网络营销思想的演变要求网络营销策略与新的环境相 ...

  2. python函数式编程思想_以python为例,浅谈函数式编程思想

    引 数据集data_socrestudentid course score 1 math 89 2 english 76 . . . 问题:求各studentid总分 方法1-一般写法: studen ...

  3. 【转载】浅谈React编程思想

    React是Facebook推出的面向视图层开发的一个框架,用于解决大型应用,包括如何很好地管理DOM结构,是构建大型,快速Web app的首选方式. React使用JavaScript来构建用户界面 ...

  4. 框架设计:浅谈ECS设计思想(一)

    从一开始的面向过程编程,再到后来面向对象编程.随着硬件性能的不断改进,用户对软件应用的要求也水涨船高.愈发庞大的应用不再是一个人或几个人的小团队能够完成的呢,分工愈来愈明显,逼迫着编程思想不断进步. ...

  5. 【转载】浅谈米之思想

    前言-----这个老师"不太正经" 我跟米老师接触也有一段时间了,总觉得有必要写一些东西了.下面我去小结米老师的思想,分享给大家. 1  让自己成为太阳,去温暖别人.要在生活中的一 ...

  6. 浅谈网络流的基本算法

    引言 过去听起来高深莫测的网络流算法,现在已飞入寻常百姓家了,对于每一个OIER,网络流是一个神圣的东西(个人见解),但神圣的同时,它并不是那样抽象,最形象的模型就是水流,从长江源点无限的向外流水,而 ...

  7. 浅谈数据结构和数据类型

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u012540337/article/details/80499226 最近总是被这两个概念混淆,抽出 ...

  8. 【python】数据结构和算法 + 浅谈单链表与双链表的区别

    有这么一句话说"程序=数据结构+算法",也有人说"如果把编程比作做菜,那么数据结构就好比食材(菜),算法就好比厨艺(做菜的技巧)". 当然这是笼统的说法,不过也 ...

  9. 浅谈python高级数据结构—— 字符串(str)

    浅谈python高级数据结构-- 字符串(str) 在python中字符串可以说的运用的特别多了.在当我们input 输入的时候,也是一个str字符串类型,我们今天就来简单的说一下(字符串)类型的一些 ...

最新文章

  1. 研发流程在敏捷开发中的详解
  2. elasticsearch数据长期保存的方案
  3. Linux如何在线修改hostname
  4. 请举例说明@Qualifier 注解?
  5. Anaconda 的安装、环境变量配置及使用
  6. linux lvm 删除pv磁盘,如何安全的删除Linux LVM中的PV物理卷(硬盘或分区)
  7. 技术研究院006---B站自用的微服务框架——Kratos
  8. C# 关键字 virtual、override和new的用法
  9. Sklearn-RandomForest
  10. ssh服务器安装测试
  11. 20200518 如何快速画出闭环特征方程的根轨迹
  12. [建筑设计].TLF-SOFT-SOFTPLAN.V13.33.bin Flaresim
  13. ICON设计的7个实用原则
  14. AI时代的全链路监控(阿里)
  15. 注册微信公众号需要哪些材料?
  16. 芯片前端设计面经笔经总结
  17. AI人工智能简介和其定义
  18. JVM优化之 -Xss -Xms -Xmx -Xmn 参数设置
  19. 绝境求生一直显示服务器忙,每日环境简报0611:潜行者绝境求生,瓦莉拉绝不认输!...
  20. R语言如何向向量中追加一个元素?

热门文章

  1. 月入3万多的Python金融量化分析师究竟多香?
  2. go学习 --- 继承
  3. 六一儿童节带给我们的思考
  4. Python同步赋值:x, y = y, x
  5. uniapp开发app——nvue
  6. 有关WCHAR(转)
  7. then的格式及执行逻辑
  8. Python经典前端框架:Django,第三天【Django基础教学--模型】
  9. 今日话题:苹果已向华为缴纳数亿美元专利费?
  10. MYSQL 之 分库分表