舞蹈链算法(DLX 算法)略解
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 算法)略解相关推荐
- 链表上的舞者——舞蹈链(DLX算法)
在整个求解过程中,指针在数据间跳跃着,就像精巧设计的舞蹈一样,故Donald E.Knuth把它称为Dancing Links(中文译名舞蹈链). 目录 第一部分:DLX算法的提出 1.1一类被称为精 ...
- 舞蹈链java实现_舞蹈链(DLX) - osc_kpp7htz3的个人空间 - OSCHINA - 中文开源技术交流社区...
#舞蹈链(DLX) Tags:搜索 ##作业部落 ##评论地址 ##一.概述 特别特别感谢这位童鞋His blog 舞蹈链是一种优美的搜索,就像下面这样跳舞- 舞蹈链用于解决精确覆盖或者重复覆盖的问题 ...
- 浅谈舞蹈链(DLX)
前言 舞蹈链的名字真好玩- 文章目录 前言 一.舞蹈链概述 二.舞蹈链例题 总结 一.舞蹈链概述 舞蹈链 (Dancing links),也叫 DLX ,是由 Donald Knuth 提出的数据结构 ...
- DLX (Dancing Links/舞蹈链)算法——求解精确覆盖问题
精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合 ...
- dancing links x(舞蹈链算法)详解
dancing links x 详解 大佬万仓一黍的blog 夜深人静写算法(九)- Dancing Links X(跳舞链) 精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合, ...
- python跳舞的线_舞蹈链(Dance Link X)算法详解及python实现
这两天打算做个数独玩玩,查了一下解数独最好的算法叫舞蹈链:Dance Link X 该算法主要是解决精确覆盖问题:比如有个集合X,以及其若干子集的集合Y,要求出一个Y的子集Y*,能够恰好分割X. 举个 ...
- 【转载】浅入 dancing links x(舞蹈链算法)
转载自原文出处 浅入 dancing links x(舞蹈链算法) abastract:利用dancing links 解决精确覆盖问题,例如数独,n皇后问题:以及重复覆盖问题. 要学习dacning ...
- Dancing Links算法(舞蹈链)
原文链接:跳跃的舞者,舞蹈链(Dancing Links)算法--求解精确覆盖问题 作者:万仓一黍 出处:http://grenet.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但 ...
- 算法帖——用舞蹈链算法(Dancing Links)求解俄罗斯方块覆盖问题
问题的提出:如下图,用13块俄罗斯方块覆盖8*8的正方形.如何用计算机求解? 解决这类问题的方法不一而足,然而核心思想都是穷举法,不同的方法仅仅是对穷举法进行了优化 用13块不同形状的俄罗斯方块(每个 ...
最新文章
- 画蛇添足之error of activesync over usb link to pc
- DbVisualizer数据库连接工具默认查询结果只显示100条解决方法,dbvis如何展示更多行,如何显示全部数据
- java memcached 存储对象_memcached—向memcached中保存Java实体需注意的问题
- NODE_PATH的疑难杂症(转)
- 牛客网 最短路 Floyd算法 Dijkstra算法 Java大数
- quick-cocos2d-x api构建文档
- OAuth2.0在项目中的应用
- 计算机应用基础教案本中职,计算机应用基础教案:计算机概述(中职教育)
- ios 阅览器html5,HTML5测试:iOS 8浏览器Safari提升明显
- shell 文件内容替换 sed用法
- Linux进阶之路————Linux磁盘分区与挂载
- 魔板(洛谷-P2730)
- 《Oracle DBA工作笔记》第一章
- 编译或者运行找不到库解决
- InnoDB 行格式
- 嵌入式学习二:怎么学习Linux操作系统
- C语言实战--解二元一次方程
- 单片机时钟和闹钟设置,串口通信
- 新特汽车在重庆“复活”:打造新品牌“电动屋”,已获网约车牌照
- 联邦学习模型鲁棒性攻击