名字:

\(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]python020在游戏中体验数值自由_勇闯地下城_终端文字游戏

    继续运行 回忆上次内容 上次使用shell环境中的命令 命令 作用 cd 改变文件夹 pwd 显示当前文件夹 ls 列出当前文件夹下的内容 最终 进入 目录 找到 游戏   如果git clone 根 ...

  2. [oeasy]python0090_极客起源_wozniac_苹果公司_Jobs_Wozniac

    极客起源 回忆上次内容 上次回顾了 DEC公司的兴起 从IBM的大型机 到DEC的小型机Mini Computer 再到DEC的终端 VT-100 计算机基础元器件发生了进化 从ENIAC的 电子管 ...

  3. C#:SqlSugar中时间戳(TimeStamp)的使用

    1.数据库建表 CREATE TABLE dbo.Test ( tId INT IDENTITY NOT NULL , tName NVARCHAR (20) NOT NULL , tSalary D ...

  4. python网络通信:IP/端口基础知识

    1.学习网络编程的目的 将多个设备通过网络连接在一起,进行数据共享 2.IP地址 作用:在逻辑上标记一台电脑 特点:没有重复的 3.通过收发数据理解IP地址的作用 dest ip 表示目的ip/src ...

  5. ComfyUI插件:ComfyUI layer style 节点(一)

    前言: 学习ComfyUI是一场持久战,而ComfyUI layer style 是一组专为图片设计制作且集成了Photoshop功能的强大节点.该节点几乎将PhotoShop的全部功能迁移到Comf ...

  6. ComfyUI插件:ComfyUI layer style 节点(二)

    前言: 学习ComfyUI是一场持久战,而ComfyUI layer style 是一组专为图片设计制作且集成了Photoshop功能的强大节点.该节点几乎将PhotoShop的全部功能迁移到Comf ...

  7. 【微信小程序】 列表查询功能

    对应本地生活案例: https://www.bilibili.com/video/BV1834y1676P?p=52 HTML代码部分: 就是普通的wx-for指令遍历 <!--pages/cl ...

  8. AI围棋项目:KataGo

    网站地址: https://katagotraining.org/ 项目地址: https://github.com/lightvector/KataGo

  9. 树莓派命令行配置wifi密码时如何实现密码加密(密文,非明文)

    首先需要知道,树莓派系统(原生系统)的wifi配置文件为: /etc/wpa_supplicant/wpa_supplicant.conf 配置该文件时,psk属性为密码,往往我们都是使用明文来进行配 ...

  10. 安装python库roboschool运行报错:ImportError: libpcre16.so.3: cannot open shared object file——解决方法

    如题: 运行roboschool运行报错. 操作系统: Ubuntu ImportError: libpcre16.so.3: cannot open shared object file: No s ...