严格意义上来说Dancing link并不是一个算法,而是一种特殊的,可以用来表示矩阵的这样一个数据结构,也就是说,它不是“解决方法”,它是提供一个可以找到更优“解决方法”的“结构”,而针对转化成这种结构之后的方法方式,其实就可以成为Dancing link配套的算法,所以我们首先要明确的模型是,它的结构特性,也就是说,建立一个特殊的数据结构,而不是方法步骤
Dancing link主要解决的是矩阵的精准覆盖问题,就我现在接触到的方向来说,如果一个问题,它与坐标系有关系,甚至说,与迷宫,和一个区域内的特点问题,其实都是可以转化为精准覆盖问题,实际上对于所有的这些题目,都需要先转化成精准覆盖问题。那么什么是精准覆盖问题?
精准覆盖问题
精准覆盖问题的定义
给定一个由0-1组成的矩阵,询问是否能够找到一个行的集合,使得集合中的每一列都恰好由一个一。
也就是说
如图所示的一个矩阵
在矩阵中的集合就应该是第一,第四,第五行,将这三行合并,并且去掉所有的0之后,能出现一行全部都是1的行,这就是一个精准覆盖,通过不同的行的使用,最后能获得一个全覆盖的面
精准覆盖问题的解决思路推导
首先我们看,当拿到这样的一个矩阵的时候我们该怎么做?
假设我们先选取第一行,那么可以看到,其实第三,第五,第六列是可以直接无视掉了,同时把这些列上有冲突的行就可以直接排除了。其实有点儿像当初玩的泡泡堂里面的炸弹一样,同行同列的直接全部都炸没了,如果途中炸到了人(1)那么就把那一行也给抹除掉。
然后剩下哪些元素呢。我们可以看到,第三行和第六行其实是和第一行有冲突的,所以第三行和第六行就直接被排除在外了,因为我们已经选取了第一行,所以第一行的其他所有元素也应该被抛出。
那剩下的应该就是2,4,5行,同时哪些被炸掉的列也就可以删去,相当于我们就获得了一个更小的矩阵(想想,为什么哪些列也就可以去掉了?)
这个时候如果我们再选择第一行,会发现第二第三行都有冲突,而剩下来的第一行并不是全部都是一,说明了第一行不是正解,那么我们就递归调用到下一行,选择第二行然后把第一行炸掉,第二行也抹除掉之后
这个时候说明了我们选择的就应该是第一幅图里面的1,4,5行使得整个矩阵得以精准覆盖。(想一想,为什么剩下的一行里面如果全部都是一的话就可以认为它是最后的答案?)
让我们理一理逻辑
这样的话,精准矩阵覆盖问题应该要通过什么样的步骤解呢?
- 从矩阵中选择一行
- 像炸弹一样把与这一行相关的那一些行列全部都炸没了
- 对于新矩阵,如果之前是空的,而之前已经有一行全都是1了,那么完美,如果不是的话,就回溯到上一次选择中。而如果没有矩阵课回溯,好了,无解了。而如果不是空矩阵,那就继续选啊。
- 不断递归
好了,问题在哪。
- 如果面对一个稍微大一点点的矩阵,对于每一个行都需要进行递归的话,怕是需要6400G的内存
- 怎么样缓存矩阵和相关的行列数据
- 在不断地矩阵转换中如何确定现在的行列和最初的矩阵的行列的问题
(虽然我认为其实最重要的时间和空间的浪费量会爆炸)
Dancing link
Dancing link 中每一个变量都有六个分量,他们分别是Left,Right,Up,Down,Col,Row;(想一想他们有什么作用?)
通过前四个变量将单个变量与其他的变量进行相连,而如果需要炸掉这一列和行的时候,直接遍历掉这一列和行就可以让这一列和行在双链表里面消失了。比如说有一个节点B
B.left = A;
B.right = C;
A.right = B;
C.left = B;
如果需要把B抹除掉怎么办?
A.right = C;
C.left = A;
B的不需要改变,想一下为什么?
剩下的Row和Col分量就是将这个改变的点的行和列记录下来。
如果需要把行列炸掉,那遍历一遍列,再遍历一遍行,让本来和它相邻的变量互相变成左右上下就可以了。
这个时候我们就需要辅助数组了,因为如果只是将点不断拆掉的话,其实是没有办法去知道何时终止的,所以我们需要一个Head来使他推出,再来一个Col变量,这样子我们就知道哪些列已经有一而有哪些列还没有,如果最后哪个Col还剩下来,Head指向的是这个Col,那么很简单,现在就没有到完美的答案,可以回溯或者输出不可能拉。
那,上面提到了更改不涉及变量本身,而涉及它的相邻变量,为什么?
因为回复的时候只需要逮到那个变量,看一下本来和它相连的是谁,把那四个小王八拿出来,重新和它建立连接就可以了嘛
来波图
看不懂的过来问我。。我看看有哪些地方写得比较丑,那应该就是我的思维盲区,谢谢