名字:

\(DL\),\(Dancing\space Link\),舞蹈链,是由\(Donald\space Knuth\)提出的数据结构,用来优化 \(X\) 算法,所以叫\(DLX\)

\(X\)算法详解

用于求解精确覆盖问题,精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1

例:

\(A,B,C \subseteq S\)

\(A \space \cap \space B= Ø\)\(A \space \cup \space B=S\)则集合\(A,B\)是问题的一种解

\(X\)算法求解模拟:

给出矩阵\(A\)

\[A=\left(
\begin{matrix}
0&0&1&0&1&1&0\\1&0&0&1&0&0&1\\0&1&1&0&0&1&0\\1&0&0&1&0&0&0\\0&1&0&0&0&0&1\\0&0&0&1&1&0&1
\end{matrix}
\right)
\]

选择第一列:

\[\left(
\begin{matrix}
0&0&1&0&1&1&0\\\\\\\\\\
\end{matrix}
\right)
\]

将有\(1\)的列向下延伸,若该行有\(1\),标记该行,处理\(A\)矩阵,得到\(B\)矩阵

\[B=\left(
\begin{matrix}
0&0&1&0&1&1&0\\&&0&&0&0\\0&1&1&0&0&1&0\\&&0&&0&0\\&&0&&0&0\\0&0&0&1&1&0&1
\end{matrix}
\right)
\]

\(S矩阵=A矩阵-B矩阵\)(删除所标记的行和列)

\[A_1=\left(
\begin{matrix}
1&0&1&1\\1&0&1&0\\0&1&0&1
\end{matrix}
\right)
\]

再选择第一列

\[\left(
\begin{matrix}
1&0&1&1\\\\\\
\end{matrix}
\right)
\]

标记

\[B_1=
\left(
\begin{matrix}
1&0&1&1\\1&&1&0\\0&1&0&1
\end{matrix}
\right)
\]

得到新矩阵,又得到一个规模较小的精确覆盖问题

\[S_1=
\left(
\begin{matrix}
0
\end{matrix}
\right)
\]

发现\(A\)矩阵不是空的,也没有一列有\(1\)(无法继续操作)

回溯

\[A_1=\left(
\begin{matrix}
1&0&1&1\\1&0&1&0\\0&1&0&1
\end{matrix}
\right)
\]

不能尝试第一行,标记第二行,尝试继续拓展

\[B_2=
\left(
\begin{matrix}
1&0&1&1\\1&0&1&0\\0&&0
\end{matrix}
\right)
\]

同理可得

\[A_3=
\left(
\begin{matrix}
1&1
\end{matrix}
\right)
\]

\(A_3\)中只有\(1\)行,且都是\(1\),选择这行,问题就可以解决

故,问题的解就是矩阵\(A\)中的第一行、矩阵\(A_1\)中的第\(2\)行、矩阵\(A_3\)的第一行,即矩阵\(A\)中的第\(1、4、5\)行

言归正传,DLX又是什么东西呢

介绍DL,舞蹈链

建出形如这样的交叉十字循环双向链只对有\(1\)的连边,这张图对应着矩阵\(A\)

获取\(head.right\)元素,即元素\(C1\),标示元素\(C1\)为紫色

先尝试行\(2\),标示该行中其他元素\(元素5和元素6\)所在的列首元素为橙色,即元素\(C4\)和元素\(C7\)

移除橙色部分和紫色部分,剩下的如图所示

获取\(head.right\)元素,即元素\(C2\),标示元素\(C2\)为紫色

列\(C2\)只有元素\(7\)覆盖,故答案只能选择行\(3\)

选择行\(3\),同理,标示元素\(C3\)和标示元素\(C6\)为橙色

移除,得

没有元素能覆盖到\(C5\),说明求解失败,回溯

回标列首元素,其顺序是标示元素的顺序反过来,即回标列首C6、回标列首C3、回标列首C2、回标列首C7、回标列首C4

故不能选择行\(2\),尝试行\(4\),同理,标示元素\(C4\)为橙色

移除,得

获取\(head.right\)元素,标示元素\(C2\)为紫色

选择行\(3\),同理,标示元素\(C3\)和\(C6\)为橙色

同理,得

同理,回溯,回标列首C6、回标列首C3

这次选择行\(5\),标示元素\(C7\)为橙色

移除,得

获取\(head.right\)元素,标示元素\(C3\)为紫色

只有元素\(1\)覆盖,故答案只能选择行\(3\),同理,标示元素\(C5\)和元素\(C6\)为橙色

移除,得

因为\(head.right=head\),故求解结束,答案栈中的答案分别是\(4、5、1\),表示该问题的解是第\(4、5、1\)行覆盖所有的列,即下图蓝色的部分

总结\(Dancing Links\)的求解

  • 函数入口
  • 判断$head.right \space ?= head $,若成立,输出答案,返回已解决,退出函数
  • 获得\(head.right\)的元素\(C\)
  • 标示元素\(C\)
  • 获得元素\(C\)所在列的一个元素
  • 标示该元素同行的其他元素所在的列首元素
  • 获得一个简化问题,递归,若返回已解决,则退出函数
  • 若刚刚尝试的不行,回标该元素同行的其他元素所在的列首元素,回标顺序与之前标示的顺序相反
  • 获得元素\(C\)所在列的下一个元素,若有,跳转“标示该元素同行的其他元素所在的列首元素”
  • 若没有,回标元素\(C\),返回未解决,退出函数

例题 P4929

代码实现

#include"bits/stdc++.h"
using namespace std;
const int N=250015;//N*M
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
inl int read(void)
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
int l[N],r[N],up[N],down[N],col[N],row[N];//每个点的左右上下指针,以及每个点所在的行列
int head[N];int sz[N];//每行的头结点,每列的节点数
int ans[N];//答案栈
int n,m,cnt;
void build(int m)
{
for(int i=0;i<=m;i++) r[i]=i+1,l[i]=i-1,up[i]=down[i]=i;
r[m]=0,l[0]=m;
memset(sz,0,sizeof sz);
cnt=m+1;//已经处理了0行,从第一行开始insert
}
void insert(int R,int C)//在R行C列插入点
{
sz[C]++;
row[++cnt]=R,col[cnt]=C;
up[cnt]=C,down[cnt]=down[C];
up[down[C]]=cnt,down[C]=cnt;
if(!head[R]) head[R]=r[cnt]=l[cnt]=cnt;
else r[cnt]=head[R],l[cnt]=l[head[R]],r[l[head[R]]]=cnt,l[head[R]]=cnt;
}
void remove(int C)//删除C列的集合
{
r[l[C]]=r[C],l[r[C]]=l[C];
for(int i=down[C];i!=C;i=down[i])
for(int j=r[i];j!=i;j=r[j])
up[down[j]]=up[j],down[up[j]]=down[j],sz[col[j]]--;
}
void resume(int C)//恢复C列的集合
{
for(int i=up[C];i!=C;i=up[i])
for(int j=l[i];j!=i;j=l[j])
up[down[j]]=down[up[j]]=j,sz[col[j]]++;
r[l[C]]=C,l[r[C]]=C;
}
bool dance(int dep)
{
if(r[0]==0)//head.right=head
{
for(int i=0;i<dep;i++) printf("%d ",ans[i]);
return 1;
}
int C=r[0];
for(int i=r[0];i;i=r[i]) if(sz[i]<sz[C]) C=i;//找到点最少的列(优化)
remove(C);
for(int i=down[C];i!=C;i=down[i])
{
ans[dep]=row[i];//压入答案栈
for(int j=r[i];j!=i;j=r[j]) remove(col[j]);
if(dance(dep+1)) return 1;
for(int j=l[i];j!=i;j=l[j]) resume(col[j]);
}
resume(C);
return 0;
}
int main(void)
{
n=read(),m=read();
int s;
build(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
s=read();
if(s) insert(i,j);
}
if(!dance(0)) puts("No Solution!");//无解
return 0;
}

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

  1. 舞蹈链 DLX

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

  2. JS function 是函数也是对象, 浅谈原型链

    JS function 是函数也是对象, 浅谈原型链 JS 唯一支持的继承方式是通过原型链继承, 理解好原型链非常重要, 我记录下我的理解 1. 前言 new 出来的实例有 _proto_ 属性, 并 ...

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

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

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

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

  5. 浅谈树链剖分 F&Q

    这是一篇迟来的博客,由于我懒得写文章,本篇以两个问题阐述笔者对树链剖分的初步理解. Q1:树链剖分解决什么问题? 树链剖分,就是把一棵树剖分成若干连续的链,将这些链里的数据映射在线性数组上维护.比方说 ...

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

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

  7. 浅谈树链剖分(C++、算法、树结构)

    关于数链剖分我在网上看到的有几个比较好的讲解,本篇主要是对AC代码的注释(感谢各位witer的提供) 这是讲解 http://www.cnblogs.com/kuangbin/archive/2013 ...

  8. 蒟蒻浅谈树链剖分之一——两个dfs操作

    树链剖分,顾名思义就是将树形的结构剖分成链,我们以此便于在链上操作 首先我们需要明白在树链剖分中的一些概念 重儿子:某节点所有儿子中子树最多的儿子 重链:有重儿子构成的链 dfs序:按重儿子优先遍历时 ...

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

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

  10. POJ3076 Sudoku 舞蹈链 DLX

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

随机推荐

  1. [oeasy]python0088_字节_Byte_存储单位_KB_MB_GB_TB

    编码进化 回忆上次内容 上次 回顾了 字符大战的结果 ibm 曾经的 EBCDIC 由于字符不连续的隐患 导致后续 出现 无数问题 无法补救 7-bit 的 ASA X3.4-1963 字母序号连续 ...

  2. QT 的 ModelView

    QApplication a(argc, argv); QDirModel model;    //QDirModel,   问文件目录树 QTreeView tree;    QListView l ...

  3. OpenGL之ShadowMap

    流程:先创建一个RenderTexture,然后用灯光的视口渲染. 然后切换到正常相机,进行渲染,使用RenderTexture中的深度或者颜色纹理,然后还原当前顶点在灯光中的深度,两者对比,比缓存中 ...

  4. 【工具】SpringBoot项目如何查看某个maven依赖是否存在以及依赖链路

    当我在SpringBoot项目中想加个依赖,但是不确定现有依赖的依赖的依赖.....有没有添加过这个依赖,怎么办呢?如果添加过了但是不知道我需要的这个依赖属于哪个依赖的下面,怎么查呢? IDEA中提供 ...

  5. 新项目加入mybatisplus,我给自己挖了个坑 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found) 上述问题的解决办法:1首先看看@mapp ...

  6. 如何在mysql中删除重复数据

    #分组去重法 讲重复的列进行分组 之后用min(id) #取其中最小的保留,其余的删除 -- 步骤 1: 创建临时表,保存每组最小的ID CREATE TEMPORARY TABLE tmp_keep ...

  7. 【Java】系统找不到指定路径

    报错信息: 2021-05-26 13:50:11,737 RMI TCP Connection(3)-127.0.0.1 ERROR DefaultRolloverStrategy contains ...

  8. ubuntu 配置NTP时间服务器&&定时同步本地时间

    Ubuntu系统: NTP时间服务器的安装: 服务器安装命令: sudo apt-get install ntp NTP服务配置,设置为本地时间服务器,屏蔽默认server,服务器层级设为10: vi ...

  9. python 中 ctypes 的使用尝试

    最近在看Python的性能优化方面的文章,突然想起ctypes这个模块,对于这个模块一直不是很理解,不过再次看完相关资料有了些新的观点. ctypes 这个模块个人观点就是提供一个Python类型与C ...

  10. 如何关闭NVIDIA显卡的CUDA运算功能

    基本很少有人会想到要关闭NVIDIA显卡的CUDA计算功能,这东西不想用就不跑cuda代码不就可以了吗,但是作为一个深度学习服务器集群的管理者来说,这个功能还是有用处的.前一阵使用实验室的一台服务器的 ...