舞蹈链(DLX)

Tags:搜索

作业部落

评论地址


一、概述

特别特别感谢这位童鞋His blog

舞蹈链是一种优美的搜索,就像下面这样跳舞~



舞蹈链用于解决精确覆盖或者重复覆盖的问题

你可以想象成贪吃蛇的一个上下左右联通的地图

\(Dancing Links\)就是通过链表这样实现的

网上有图的博客

二、实现

更详细的讲解在课件中

精确覆盖

精确覆盖大概指的就是数独和八皇后那样的问题

矩阵中选择一个行的集合,使得每列有且只有一个1

那么就是说每个格子上的点都有若干限制条件(行、列、对角线),每个条件都只允许一个元素

在舞蹈链中(可以把它看作一个表格),每个元素看作一行,限制条件转化为列,选一行删去也同时要删去这一行中所有点所在的列

然后舞蹈链兹瓷快速删除这些东西和快速回溯(复杂度未知)

大概有\(init\)、\(link\)、\(remove\)、\(resume\)、\(dance\)五个函数

实现的话看代码吧,有详细的注释

Code - [luogu1219]八皇后

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. using namespace std;
  6. const int N=100100;
  7. int ans,nn,o;
  8. struct out{int a[14];}Ans[N];
  9. namespace DLX
  10. {
  11. int n,m,cnt;//长宽,点的数量
  12. int l[N],r[N],u[N],d[N];//上下左右的情况
  13. int row[N],col[N];//每个点所处的行列
  14. int h[N],s[N];//头节点和每列节点数
  15. int ansk[20];//答案
  16. void init(int nn,int mm)
  17. {
  18. //这个表格被循环套了起来,就像贪吃蛇的地图,左右和上下相通
  19. //预先给第0行的每一列弄一个点
  20. n=nn,m=mm;
  21. for(int i=0;i<=m;i++)
  22. r[i]=i+1,l[i]=i-1,u[i]=d[i]=i;
  23. r[m]=0;l[0]=cnt=m;
  24. memset(h,-1,sizeof(h));
  25. }
  26. void link(int R,int C)//在R行C列插入点
  27. {
  28. s[C]++;cnt++;//先记录这个点的各种信息
  29. row[cnt]=R; col[cnt]=C;
  30. //把列的链表改动
  31. u[cnt]=C;
  32. d[cnt]=d[C];
  33. u[d[C]]=cnt;
  34. d[C]=cnt;
  35. //把行的链表改动
  36. if(h[R]<0) h[R]=l[cnt]=r[cnt]=cnt;
  37. else
  38. {
  39. r[cnt]=h[R];
  40. l[cnt]=l[h[R]];
  41. r[l[h[R]]]=cnt;
  42. l[h[R]]=cnt;
  43. }
  44. }
  45. void remove(int C)//删除C列以及C列上有点的行
  46. {
  47. r[l[C]]=r[C]; l[r[C]]=l[C];
  48. for(int i=d[C];i!=C;i=d[i])
  49. for(int j=r[i];j!=i;j=r[j])
  50. {
  51. u[d[j]]=u[j];
  52. d[u[j]]=d[j];
  53. s[col[j]]--;//是减得只剩下1吗(dei)
  54. }
  55. }
  56. void resume(int C)//恢复C列以及C列上有点的行
  57. {
  58. r[l[C]]=C; l[r[C]]=C;
  59. for(int i=d[C];i!=C;i=d[i])
  60. for(int j=r[i];j!=i;j=r[j])
  61. {
  62. u[d[j]]=j;
  63. d[u[j]]=j;
  64. s[col[j]]++;
  65. }
  66. }
  67. void dance(int deep)
  68. {
  69. int C=r[0];//找第一个限制条件
  70. if(C>2*nn)//如果所有的行已经被删完就统计答案(能不能>2n)
  71. {
  72. ans++;
  73. for(int i=0,x,y;i<deep;i++)
  74. {
  75. //记录下来选的点的编号,用编号还原行列
  76. x=ansk[i]%nn;
  77. y=(ansk[i]-1)/nn+1;
  78. if(x==0) x=nn;
  79. Ans[ans].a[y]=x;//x和y是等价的,可以交换
  80. }
  81. return;
  82. }
  83. for(int i=C;i<=nn;i=r[i])//找到点最少的列
  84. /*
  85. 这是一处剪枝,因为删掉点最少的列,就是为了满足这个限制条件
  86. 需要枚举删掉的点就少一些,从而使得之后的剪枝更高效
  87. 相当于把搜索树繁茂的地方留给叶子,而深度越深越容易被剪枝
  88. 不加会T
  89. */
  90. if(s[i]<s[C]) C=i;
  91. remove(C);//删掉这一列
  92. for(int i=d[C];i!=C;i=d[i])//枚举答案是这一列的哪个点,因为每一列只能选一个点,所以枚举选哪个
  93. {
  94. ansk[deep]=row[i];//记录答案,这个点编号是row[i]
  95. for(int j=r[i];j!=i;j=r[j]) remove(col[j]);//这个点的行也得删了,把这行有点的列也删掉
  96. dance(deep+1);
  97. for(int j=r[i];j!=i;j=r[j]) resume(col[j]);//回溯
  98. }
  99. resume(C);//回溯过程
  100. }
  101. }
  102. int cmp(const out&A,const out&B)
  103. {
  104. int p=0;while(A.a[p]==B.a[p]) p++;
  105. return A.a[p]<B.a[p];
  106. }
  107. int main()
  108. {
  109. /// freopen("a.out","w",stdout);
  110. scanf("%d",&nn);
  111. /*
  112. nn*nn个格子,每个格子看作舞蹈链的一行
  113. 总共有nn行nn列nn×2-1左对角nn×2-1右对角 共6×nn-2个限制
  114. 把每个限制看作一列,进行精准覆盖
  115. */
  116. DLX::init(nn*nn,6*nn-2);
  117. for(int i=1;i<=nn;i++)
  118. for(int j=1;j<=nn;j++)
  119. {
  120. o++;
  121. DLX::link(o,i);//占据第i行
  122. DLX::link(o,j+nn);//占据第j列(能不能不写这一句)
  123. DLX::link(o,i-j+3*nn);//占据第i-j+nn个左上到右下的对角线
  124. DLX::link(o,i+j+4*nn-2);//占据第i+j-1个右上到左下的对角线
  125. }
  126. DLX::dance(0);//跳舞啦
  127. sort(Ans+1,Ans+ans+1,cmp);
  128. for(int i=1;i<=3;i++,puts(""))
  129. for(int j=1;j<=nn;j++) printf("%d ",Ans[i].a[j]);
  130. printf("%d\n",ans);return 0;
  131. }

Code - Easy Finding戳我

重复覆盖

矩阵中选择一个行的集合,使得每列至少有一个1

所以选了一列之后不能把列中有1的所有的行删掉,复杂度会提高,加一个估价函数的\(A*\)剪枝

Code - [FZU1686]神龙的难题

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. using namespace std;
  6. const int N=70000;
  7. int A[20][20],n,m,n1,m1;
  8. int o,ans,tt;
  9. namespace DLX
  10. {
  11. int n,m,p,u[N],d[N],l[N],r[N];
  12. int col[N],row[N],h[300],s[300],vis[300];
  13. void init(int nn,int mm)
  14. {
  15. n=nn,m=mm;
  16. for(int i=0;i<=m;i++)
  17. l[i]=i-1,r[i]=i+1,d[i]=u[i]=i,s[i]=0;
  18. p=m;l[0]=m;r[m]=0;
  19. memset(h,-1,sizeof(h));
  20. }
  21. void link(int R,int C)
  22. {
  23. p++;row[p]=R;col[p]=C;s[C]++;
  24. d[p]=C;u[p]=u[C];
  25. d[u[C]]=p;u[C]=p;
  26. if(h[R]<0) h[R]=l[p]=r[p]=p;
  27. else r[p]=h[R],l[p]=l[h[R]],r[l[h[R]]]=p,l[h[R]]=p;
  28. }
  29. void remove(int C)
  30. {
  31. for(int i=d[C];i!=C;i=d[i])
  32. l[r[i]]=l[i],r[l[i]]=r[i];
  33. }
  34. void resume(int C)
  35. {
  36. for(int i=u[C];i!=C;i=u[i])
  37. l[r[i]]=i,r[l[i]]=i;
  38. }
  39. int H()
  40. {
  41. int res=0;
  42. memset(vis,0,sizeof(vis));
  43. for(int i=r[0];i;i=r[i])
  44. {
  45. if(vis[i]) continue;
  46. vis[i]=1; res++;
  47. for(int j=d[i];j!=i;j=d[j])
  48. for(int k=r[j];k!=j;k=r[k])
  49. vis[col[k]]=1;
  50. }
  51. return res;
  52. }
  53. void dance(int step)
  54. {
  55. if(step+H()>=ans) return;
  56. if(r[0]==0) {ans=min(ans,step);return;}
  57. int C=r[0];
  58. for(int i=r[C];i;i=r[i]) if(s[i]<s[C]) C=i;
  59. for(int i=d[C];i!=C;i=d[i])
  60. {
  61. remove(i);
  62. for(int j=r[i];j!=i;j=r[j]) remove(j);
  63. dance(step+1);
  64. for(int j=l[i];j!=i;j=l[j]) resume(j);
  65. resume(i);
  66. }
  67. }
  68. }
  69. int main()
  70. {
  71. while(~scanf("%d%d",&n,&m))
  72. {
  73. o=1;ans=1e9;tt=0;
  74. for(int i=1;i<=n;i++)
  75. for(int j=1,x;j<=m;j++)
  76. {
  77. scanf("%d",&x);
  78. if(x) A[i][j]=++tt;
  79. else A[i][j]=0;
  80. }
  81. scanf("%d%d",&n1,&m1);
  82. DLX::init((n-n1+1)*(m-m1+1),tt);
  83. for(int i=1;i<=n-n1+1;i++)
  84. for(int j=1;j<=m-m1+1;j++,o++)
  85. for(int x=i;x<=i+n1-1;x++)
  86. for(int y=j;y<=j+m1-1;y++)
  87. if(A[x][y]) DLX::link(o,A[x][y]);
  88. DLX::dance(0);printf("%d\n",ans);
  89. }
  90. }

三、尾声

舞蹈链的复杂度是指数级别的,但是由于有非常强大的剪枝所以可以有玄学复杂度

在一般竞赛中舞蹈链并没有很广泛的应用和考察

但是这种思想需要大家了解,体会其中舞蹈的优美

弄个题单

舞蹈链(DLX)的更多相关文章

  1. 舞蹈链 DLX

    欢迎访问——该文出处-博客园-zhouzhendong 去博客园看该文章--传送门 舞蹈链是一个非常玄学的东西…… 问题模型 精确覆盖问题:在一个01矩阵中,是否可以选出一些行的集合,使得在这些行的集 ...

  2. [学习笔记] 舞蹈链(DLX)入门

    "在一个全集\(X\)中若干子集的集合为\(S\),精确覆盖(\(\boldsymbol{Exact~Cover}\))是指,\(S\)的子集\(S*\),满足\(X\)中的每一个元素在\( ...

  3. luogu P4929 【模板】舞蹈链 DLX

    LINK:舞蹈链 具体复杂度我也不知道 但是 搜索速度极快. 原因大概是因为 每次检索的时间少 有一定的剪枝. 花了2h大概了解了这个东西 吐槽一下题解根本看不懂 只能理解大概的想法 核心的链表不太懂 ...

  4. P4929-[模板]舞蹈链(DLX)

    正题 题目链接:https://www.luogu.com.cn/problem/P4929 题目大意 \(n*m\)的矩形有\(0/1\),要求选出若干行使得每一列有且仅有一个\(1\). 解题思路 ...

  5. Vijos1755 靶形数独 Sudoku NOIP2009 提高组 T4 舞蹈链 DLX

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目(传送门) 题意概括 给出一个残缺的数独,求这个数独中所有的解法中的最大价值. 一个数独解法的价值之和为每个位置所填的数值 ...

  6. POJ3076 Sudoku 舞蹈链 DLX

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目(传送门) 题意概括 给出一个残缺的16*16数独,求解. 题解 DLX + 矩阵构建  (两个传送门) 学完这个之后,再 ...

  7. POJ3074 Sudoku 舞蹈链 DLX

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目(传送门) 题意概括 给出一个残缺的数独,求解. 题解 DLX + 矩阵构建  (两个传送门) 代码 #include & ...

  8. POJ2676 Sudoku 舞蹈链 DLX

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目(传送门) 题意概括 给出一个残缺的数独,求解.SPJ 题解 DLX + 矩阵构建  (两个传送门) 代码 #includ ...

  9. 关于用舞蹈链DLX算法求解数独的解析

    欢迎访问——该文出处-博客园-zhouzhendong 去博客园看该文章--传送门 描述 在做DLX算法题中,经常会做到数独类型的题目,那么,如何求解数独类型的题目?其实,学了数独的构建方法,那么DL ...

  10. POJ3740 Easy Finding 舞蹈链 DLX

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目 精确覆盖问题模板题 算法 DLX算法 学习DLX算法--传送门 代码 #include <cstring> ...

随机推荐

  1. 区块链 blockchain

    区块链是去中心化的记账方式.没有中心,安全,高效.区块链是属于分布式计算的一种.是一种数据库. 区块链不是什么比特币,xx币.而是比特币他们用了区块链的技术. 区块链具有去中心化.无须中心信任.不可篡 ...

  2. vue-cli 项目优化之3种方法对比:本地静态库资源(推荐)、cdn、DllPlugin

    vue-cli 项目优化之3种方法对比:本地静态库资源(推荐).cdn.DllPlugin 事项 本地静态库资源 cdn DllPlugin 依赖 依赖cdn网站资源(有种完善方法:如果cdn引入不成 ...

  3. react:路由登陆后才能访问的控制

    react-router 通过创建一个 需要认证的路由 来限制登陆后才能访问. 官方例子:https://reacttraining.com/react-router/web/example/auth ...

  4. InvokeRequired和Invoke(转)

    C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它.此时它将会在内部调用ne ...

  5. java -jar 执行jar包出现 java.lang.NoClassDefFoundError

    我用idea工具将自己开发java程序打成一个可执行的jar包,当然用eclipse或者直接用jar命令行都无所谓,本质都是将程序归档到一个压缩包,并附带一个说明清单文件. 打jar的操作其实很简单, ...

  6. CentOS7创建本地yum源

    [root@master ~]# mkdir -p /var/www/html 使用安装系统的ISO镜像文件CentOS-7-x86_64-Everything-1611.iso 把CentOS-7- ...

  7. 阅读<Video Timing Controller>笔记

    阅读<Video Timing Controller>笔记   1.Video Timing Controller Block Diagram 2.Example Video Timing ...

  8. 【转】python文件和目录操作方法大全(含实例)

    python文件和目录操作方法大全(含实例) 这篇文章主要介绍了python文件和目录的操作方法,简明总结了文件和目录操作中常用的模块.方法,并列举了一个综合实例,需要的朋友可以参考下一.python ...

  9. ubuntu-docker入门到放弃(五)docker网络管理

    查看docker宿主机的网卡信息我们会发现,有一个docker0的网卡,这个网卡就是用于跟docker容器进行通讯的,这个网段跟我们docker容器的网段是一样的: #ifconfig docker容 ...

  10. Configure Virtual Serial Port Driver (vspd)注册表

    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VSBC7\Ports\COM3COM4] “Port1”=”COM3” “Port2”=” ...