Another Blog

DLX=Dancing Line X

之前学 DLX 时在网上看了好几篇博客,结合着几篇讲代码的、有图片的、说原理的,总算弄懂了。这里

DLX 是用来解决精确覆盖问题的算法,算是 X 算法的优化。

时间复杂度 O(玄学)O(\text{玄学})O(玄学)(最坏是指数级)

精确覆盖问题:

给定一个 N 行 M 列的矩阵,矩阵中每个元素要么是 1,要么是 0。

你需要在矩阵中挑选出若干行,使得对于矩阵的每一列 j,在你挑选的这些行中,有且仅有一行的第 j 个元素为 1。

如上表,选择第 1、4、5 行即符合要求。

X 算法

这个算法的过程如下:

矩阵为空代表得到了解。

否则首先我们找到 1 的个数最少的一列(个数最少可以使搜索次数变少),考虑为了使这一列有 1 选择哪一行。

不管我们选哪一行,这一行都不能再选;对于这一列是 1 的其它行,这一行也不能再选(否则这一列将有不止一个 1),所以我们先把这些行从原矩阵中删掉(但是数据要保留好)。同时这一列将要成功解决,为了避免重复考虑,删掉这一列。

之后把删除一列以及这一列为 1 的所有行的操作简称为删除这一列

接着我们枚举选择这一列是 1 的每一行,看选哪个:

  • 删除这一行所有是 1 的列:
  • 递归求解
  • 如果没有成功,恢复上上步删除的内容。

枚举了每一行都无解,则恢复删掉的内容,并回溯。

我们仍以上面那个矩阵为例(多图警告!):

首先我们枚举到第一列:

删除之:

我们可以删除第二行或第四行,先枚举第二行,则删除第 4、7 列。

(用了很多颜色,希望看起来清楚一些)

接着递归求解这个矩阵:

先枚举第五列,并删除之。

我们只能选第一行,于是删除第 3、5、6 列。

继续递归,枚举到 2 发现解不动,回溯。

一直回溯到原矩阵,开始尝试第四行。(下面描述从简)

删除,递归求解

枚举第五列,此时只能选择第一行

删除,递归求解

枚举第二列,选择第一行(原矩阵中是第五行)。

删除,递归求解

(空矩阵就不配图了吧)

得到了空矩阵,意味着问题解决了——选择第 1、4、5 行

DLX

DLX 在 X 算法的基础上,使用交叉十字循环双向链表优化,跑得飞快 ,但时间复杂度仍然是指数级的。

所谓交叉十字循环双向链表,大概是这样的:

其中每一个节点代表一个为 1 的元素。

实际操作时,我们还会在每一列开头添加一个虚拟节点以方便索引:

之后我们的例子都基于之前讲 X 算法用的例子,即:

每个节点有 6 个属性,分别是与其相邻的元素编号与该元素的行列。

struct node{int l,r,u,d;int col,row;node(){}node(int ll,int rr,int uu,int dd,int coll=0,int roww=0):l(ll),r(rr),u(uu),d(dd),col(coll),row(roww){}
};
node dlx[100000];//交叉十字双向循环链表(?)
int head=0,cnt=0;

初始化时仅仅处理虚拟节点:

void init(){//首先处理虚拟节点 for(int i=1;i<=n;i++)dlx[++cnt]=node(i-1,i+1,i,i,i,0);dlx[head].l=n;dlx[head].r=1;dlx[n].r=head;
}

每次插入一行,都要处理这一行内互相指的指针,以及这一行为 1 的元素往上、下指的指针。

void addrow(vector<int> a,int r){//a:为 1 的位置,r:行的编号 int beg=cnt+1;for(int i=0;i<a.size();i++){cnt++;dlx[cnt]=node(cnt-1,cnt+1,dlx[a[i]].u,a[i],a[i],r);dlx[dlx[a[i]].u].d=cnt;dlx[a[i]].u=cnt;++sz[a[i]];}dlx[cnt].r=beg;dlx[beg].l=cnt;
}

在“删除”元素时,我们实际上只是让指针不再指向该节点。若删除一整列,我们只需要将这一列的虚拟节点“删去”,并且把要删除的行的节点从列中“删去”,实际上我们还能遍历这些行与列,恢复起来也很方便。如上图中删去第一列后的效果是这样的:

【图片咕咕咕了】

删除的实现:

void remove(int x){//删除列 dlx[dlx[x].l].r=dlx[x].r;dlx[dlx[x].r].l=dlx[x].l;//先删除虚拟节点 for(int i=dlx[x].d;i!=x;i=dlx[i].d){    //再删除这一列节点所在的 for(int j=dlx[i].r;j!=i;j=dlx[j].r){//每一行的每个节点 dlx[dlx[j].u].d=dlx[j].d;       //(不删除这一列本身) dlx[dlx[j].d].u=dlx[j].u;sz[dlx[j].col]--;}}
}

恢复就是删除的逆操作:

void restore(int x){//恢复列 for(int i=dlx[x].u;i!=x;i=dlx[i].u){    //先恢复这一列节点所在的 for(int j=dlx[i].l;j!=i;j=dlx[j].l){//每一行的每个节点 dlx[dlx[j].u].d=j;dlx[dlx[j].d].u=j;sz[dlx[j].col]++;}}//再恢复虚拟节点 dlx[dlx[x].l].r=x;dlx[dlx[x].r].l=x;
}

这些基本操作准备好后,就按照 X 算法的流程跑。

bool solve(int step){if(dlx[head].r==head)return top=step-1,1;//所有列被消完  int c=dlx[head].r;for(int i=c;i!=head;i=dlx[i].r){if(sz[i]<sz[c])c=i;//寻找 size 最小的列 }remove(c);//如果我们选择了这一列,包含这一列的所有行都不能再选 for(int i=dlx[c].d;i!=c;i=dlx[i].d){//枚举选择哪一行 ans[step]=dlx[i].row;//cout<<"s "<<i<<" "<<dlx[i].row<<endl;for(int j=dlx[i].r;j!=i;j=dlx[j].r){//这一行选择后有很多列被选择,有这些列的其它行不能再被选 remove(dlx[j].col); }if(solve(step+1))return 1;//如果成功,返回 for(int j=dlx[i].l;j!=i;j=dlx[j].l){//不成功,恢复 restore(dlx[j].col); }}restore(c);//一直没有解,恢复 return 0;
}

DLX 与 数独

DLX 算法用来解决的一个很经典的问题就是数独。我们可以构造一个矩阵,通过求出其一个精确覆盖的解,来得到数独的一个解。

这个矩阵有至多 729=93729=9^3729=93 行。每一行依次代表在 i 行 j 列填入数字 k。假如这一格是留空的,那就把 k=1∼9k=1\sim 9k=1∼9 都构造对应的行,否则只给指定的 k 构造一行。

很显然,这个精确覆盖问题的解就可以对应到哪一行哪一列放哪个数。下面我们通过构造矩阵的内容来保证填出来的数独符合要求。

要求一:每个格子只能填一个数

我们构造 81=9281=9^281=92 列,每一列依次对应在 i 行 j 列填入了一个数。

这样保证选出来的任意两行都不会对应同一个格子。

要求二:每个行不能重复

我们构造 81=9281=9^281=92 列,每一列依次对应在 i 行填入了 k。

这样保证选出来的任意两行都不会同时对应数独中的同一行、是同一个数。

要求三:每个列不能重复

同理,我们构造 81=9281=9^281=92 列,每一列依次对应在 j 列填入了 k。

要求四:每个宫不能重复

同理,我们构造 81=9281=9^281=92 列,每一列依次对应在 l 宫填入了 k。


这样我们构造了一个 729 行 324 列的矩阵,对其 DLX 一下就能求解。

这里拿一个 4×44\times 44×4 的数独举了个例子:

例题:POJ2676 Sudoku

(大概是完结撒花了)

舞蹈链算法(DLX 算法)略解相关推荐

  1. 链表上的舞者——舞蹈链(DLX算法)

    在整个求解过程中,指针在数据间跳跃着,就像精巧设计的舞蹈一样,故Donald E.Knuth把它称为Dancing Links(中文译名舞蹈链). 目录 第一部分:DLX算法的提出 1.1一类被称为精 ...

  2. 舞蹈链java实现_舞蹈链(DLX) - osc_kpp7htz3的个人空间 - OSCHINA - 中文开源技术交流社区...

    #舞蹈链(DLX) Tags:搜索 ##作业部落 ##评论地址 ##一.概述 特别特别感谢这位童鞋His blog 舞蹈链是一种优美的搜索,就像下面这样跳舞- 舞蹈链用于解决精确覆盖或者重复覆盖的问题 ...

  3. 浅谈舞蹈链(DLX)

    前言 舞蹈链的名字真好玩- 文章目录 前言 一.舞蹈链概述 二.舞蹈链例题 总结 一.舞蹈链概述 舞蹈链 (Dancing links),也叫 DLX ,是由 Donald Knuth 提出的数据结构 ...

  4. DLX (Dancing Links/舞蹈链)算法——求解精确覆盖问题

    精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合 ...

  5. dancing links x(舞蹈链算法)详解

    dancing links x 详解 大佬万仓一黍的blog 夜深人静写算法(九)- Dancing Links X(跳舞链) 精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合, ...

  6. python跳舞的线_舞蹈链(Dance Link X)算法详解及python实现

    这两天打算做个数独玩玩,查了一下解数独最好的算法叫舞蹈链:Dance Link X 该算法主要是解决精确覆盖问题:比如有个集合X,以及其若干子集的集合Y,要求出一个Y的子集Y*,能够恰好分割X. 举个 ...

  7. 【转载】浅入 dancing links x(舞蹈链算法)

    转载自原文出处 浅入 dancing links x(舞蹈链算法) abastract:利用dancing links 解决精确覆盖问题,例如数独,n皇后问题:以及重复覆盖问题. 要学习dacning ...

  8. Dancing Links算法(舞蹈链)

    原文链接:跳跃的舞者,舞蹈链(Dancing Links)算法--求解精确覆盖问题 作者:万仓一黍 出处:http://grenet.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但 ...

  9. 算法帖——用舞蹈链算法(Dancing Links)求解俄罗斯方块覆盖问题

    问题的提出:如下图,用13块俄罗斯方块覆盖8*8的正方形.如何用计算机求解? 解决这类问题的方法不一而足,然而核心思想都是穷举法,不同的方法仅仅是对穷举法进行了优化 用13块不同形状的俄罗斯方块(每个 ...

最新文章

  1. 画蛇添足之error of activesync over usb link to pc
  2. DbVisualizer数据库连接工具默认查询结果只显示100条解决方法,dbvis如何展示更多行,如何显示全部数据
  3. java memcached 存储对象_memcached—向memcached中保存Java实体需注意的问题
  4. NODE_PATH的疑难杂症(转)
  5. 牛客网 最短路 Floyd算法 Dijkstra算法 Java大数
  6. quick-cocos2d-x api构建文档
  7. OAuth2.0在项目中的应用
  8. 计算机应用基础教案本中职,计算机应用基础教案:计算机概述(中职教育)
  9. ios 阅览器html5,HTML5测试:iOS 8浏览器Safari提升明显
  10. shell 文件内容替换 sed用法
  11. Linux进阶之路————Linux磁盘分区与挂载
  12. 魔板(洛谷-P2730)
  13. 《Oracle DBA工作笔记》第一章
  14. 编译或者运行找不到库解决
  15. InnoDB 行格式
  16. 嵌入式学习二:怎么学习Linux操作系统
  17. C语言实战--解二元一次方程
  18. 单片机时钟和闹钟设置,串口通信
  19. 新特汽车在重庆“复活”:打造新品牌“电动屋”,已获网约车牌照
  20. 联邦学习模型鲁棒性攻击

热门文章

  1. 敌我差距:普通男生感动自己,撩妹高手感动女生。
  2. 有两个桌面文件夹变html,Windows 8.1桌面出现两个同名文件或文件夹怎么办
  3. 机器学习算法(3)之决策树算法
  4. 快速为PPT2003的每页加上总页码
  5. 2020北京智源大会|旷视的技术探索、产业实践与可持续发展 AI 的打造
  6. 第15-16章项目1-学生成绩管理系统
  7. 远程控制如何保证安全?浅析向日葵从体系到功能两个方面的安全举措
  8. 第2章-Bluetooth® LE audio架构
  9. ORACLE-SQL性能优化-排序取第一条数据
  10. Linux 内存检测工具 memwatch的使用