ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

解题报告 (一) Dancing Links X

2021-06-03 12:55:27  阅读:235  来源: 互联网

标签:01 题意 覆盖 Dancing 题解 矩阵 Links 解题 代表


Dancing Links X 算法详解


一、精确覆盖


1) Easy Finding

  题意:给定一个M×N的01矩阵(其中M <= 16,N <=300),求一个行集合,使得每一列恰好有一个“1”。

  题解:赤裸裸的精确覆盖问题,Dancing Links的模板题,直接按照矩阵构图后上模板。


2) Treasure Map

  题意:给定一些矩形碎片(x1, y1, x2, y2),范围在[0,30]之间,要求用最少的矩形碎片拼成一个n×m(n,m <= 30)的矩形,如果没法完成,输出-1。

  题解:精确覆盖。将n×m的矩形的每个单位格(1×1的格子)看成是Dancing Links矩阵的列,每个小矩形碎片看成是矩阵的行。首先对每个单位格按照行、列优先进行编号[0, n*m)。DLX矩阵A[i][j]=1当且仅当第j个单位格是第i个矩形片的组成部分。

图2-1

  然后对DLX矩阵求一次精确覆盖即可。由于要求求最小解,每次搜索到一个可行解的时候更新最优解。如果某次搜索层数已经大于最优解,则直接返回(剪枝)。


3) Power Stations

  题意:N个小镇由M条电缆两两相连,每个镇上有1个发电站,当某个发电站开始工作,它将会提供给由电缆直接相连的邻镇,但是由于一些特殊原因,一个镇不能接受多个镇提供的电力供给。并且每个发电站有一个发电的时间区间[L, R],对于每个发电站,只能选择至多一个[L, R]的子区间的时间段进行发电,问是否存在一种方案,使得接下来的D天内每个镇都有且仅有一个发电站来供给(其中N <= 60, M <= 150, D <= 5)。

图3-1

  对于图3-1这组数据,1号镇选择第一天发电,同时供给给2号镇;2号镇选择第二、三天发电,同时供给给1号镇;3号镇全程三天发电。

  题解:精确覆盖问题。关键是建图的过程,如何把问题转化为Dancing Links的01矩阵是问题的关键。

  我们需要理清01矩阵的行和列的含义,行代表问题的所有情况,列代表问题的约束条件。我们取行的子集的过程相当于取出一些互不冲突(即约束条件互相独立)的情况进行组合来满足问题的需求,那么行的子集就是我们需要的答案。

  首先来考虑行,因为行对应了问题的所有情况,所以我们来枚举所有可能的情况,第i个发电站要么一直不发电,要么选择某一个时间段进行发电,一共两种情况:

    1)第i个发电站 选择从第j天发电到k天,根据i,j,k的取值范围,可得最大行数为N*(D+1)*D/2 = 900;

    2)第i个发电站 完全不工作,总共N个。

  所以总的最大行数为N*(D+1)*D/2 + N = 960。那么可以用(i, j, k)代表第i个发电站的工作时间为[j, k],特殊的,(i, 0, 0)代表第i个全程不工作,靠邻镇供给。

  然后考虑列,列对应了问题的约束条件:

    1)每个镇只能被自己或者1个邻镇发电:列[0, N)代表第1天N个镇的发电与否,列[N, 2N)则代表第2天的,列[(i-1)N,iN)则代表第i天的N个镇的发电情况;

    2)每个发电站只能取自己工作时间的一段子区间进行工作:用第D*N-1+i列代表第i个发电站的工作与否(靠邻镇供给的也算);

图3-2

  所以总的列数就是(D+1)*N = 360。然后枚举(i, j, k)对这个960×360的01矩阵建图,求一次精确覆盖即可。


4) NQUEEN - Yet Another N-Queen Problem

  题意:给定一个N*N的棋盘,上面放置了一些互不冲突的皇后,求放置剩余的皇后完成N皇后问题(N皇后问题:N*N的棋盘放置N个皇后,使得任意两个皇后不能互相***,不同行、不同列、不同对角线)。

  题解:经典精确覆盖问题变形。关键是建图的过程,如何把问题转化为Dancing Links的01矩阵是问题的关键。首先我们考虑没有摆放任何皇后时的解法:

  行代表问题的所有情况,列代表问题的约束条件。

  行的取值为棋盘的N*N个位置。

  列代表了四类约束条件:

     1)列[0, N)      代表了棋盘N行的占据情况

     2)列[N, 2N)     代表了棋盘N列的占据情况

     3)列[2N, 4N-1)   代表了棋盘2N-1条主对角线的占据情况

     4)列[4N-1, 6N-2)  代表了棋盘2N-1条副对角线的占据情况

  所以这是个行N*N,列6N-2的01矩阵。

图4-1

  四个约束条件分情况讨论:对于(i, j)的位置,占据的行为i;占据的列为j;主对角线可以通过i和j的相对情况来判断,所有i-j相同的占据的主对角线为N+i-j;副对角线类似,所有i+j相同的位置占据的副对角线为i+j-1;

  枚举所有情况,对每个(i, j)属于[1,N]建立对应的约束条件,就相当于建立了Dancing Links的01矩阵。

  然而,N皇后问题不能直接求精确覆盖,因为我们发现,行和列必须完全覆盖到,但是主和副对角线没有要求一定要全部覆盖,所以我们问题的求解转变成对于这样一个01矩阵,求选出一些行,使得前2N列每列恰有一个“1” (这2N列分别对应行和列的约束条件)。那么也很简单, 只需要修改两个地方:

  1) 在选择“1”元素最少的列的时候只选择[0, 2N);

  2) 如果[0, 2N)列中都已经没有“1”可以选择了,那么算法终止;

  以上就是求解N皇后问题的全部过程。

图4-2

  接下来再来看已经占据了一些格子之后,继续放置剩余棋子的N皇后问题。

  之前已经讨论了每个格子对应了矩阵的一行,那么某个格子已经放置了棋子,相当于在全局可行解中必须选这些格子对应的行,那么问题就简单了,直接模拟将这些放置了棋子的格子对应的行利用Dancing Links X算法进行删除即可,并且这部分删除是不需要的回溯的,因为它们必选。然后X算法对剩余01矩阵进行求解即可。


5) Lamp

  题意:给定N(N<= 500)个灯和M(M <= 500)个开关,灯是否亮起可以由任意某个开关的“开”或“关”来控制。给出M个开关的开关方案,使得所有灯都亮起。

  例如,1号灯亮的条件是1号开关“开”或2号开关“开”,2号灯亮的条件是1号开关“关”。那么开关选择序列可以考虑1号“关”、2号“开”。

  题解:经典精确覆盖问题变形。N皇后的简化版。还是那句话:行代表问题的所有情况,列代表问题的约束条件。那么问题的所有情况,即所有开关的“开”和“关”的状态数,情况总数为2*M。即01矩阵的每行代表每个开关的“开”或“关”。

  问题的约束条件有两个,其中第二个是隐含条件:

    1) 所有灯都亮起;

    2) 每种开关只能调一种状态;

  所有01矩阵的前N列代表在这个开关的状态下,对应的灯是否亮起(“1”代表亮,“0”代表不亮);后M列代表使用了对应的开关,也就是第i个开关代表的两行对应的列的值为“1”。

  所以总的矩阵规模为 2*M行,(N+M)列的01矩阵,对前N列求一次精确覆盖即可。最后输出方案的时候,如果代表开关状态的两行都没有选中,说明任何一种状态都不会影响最后的结果,任意输出其中一种状态即可。

图5-1

6) Dominoes

  题意:给出以下12块骨牌,每个骨牌只能用一次,但是可以旋转或者翻转,要求铺满一个M×N的棋盘,保证M×N = 60。求方案数。

图6-1

  例如,3×20的棋盘的其中一种方案如下:

图6-2

  题解:精确覆盖问题。行代表问题的所有情况,列代表问题的约束条件。

  那么行代表了每个形状的骨牌经过翻转/旋转后的在原棋盘上每格的放置方案。这里需要注意翻转和旋转后骨牌最多有8(2×4)种情况,但是如果一旦翻转或旋转得到的结果一样的话只能算一种,所以这里可以采用二进制哈希标记每种旋转/翻转状态。

  列分为两类:

    1) 每个格子是否被占据;总共60个格子,即60列;

    2) 当前骨牌放置方案用的是哪类骨牌,总共12列;

图6-3

  因为要统计所有情况,而不是找到一个解就退出搜索,所以计算过程可能会比较慢,但是输入数据只有几种,可以算出所有情况后打表。


7) 数独系列(精确覆盖)

基础题

  Sudoku Killer http://acm.hdu.edu.cn/showproblem.php?pid=1426 

  Su-Su-Sudoku http://acm.hdu.edu.cn/showproblem.php?pid=2780

  Sudoku http://acm.hdu.edu.cn/showproblem.php?pid=3111

  Sudoku http://poj.org/problem?id=2676

  Sudoku http://poj.org/problem?id=3074

  Sudoku http://poj.org/problem?id=3076

进阶题

  Sudoku http://acm.hdu.edu.cn/showproblem.php?pid=5547 N=2的数独,深搜枚举也能过

  Sudoku http://acm.hdu.edu.cn/showproblem.php?pid=3476 N=2的数独,枚举所有情况打表

  Sudoku http://acm.hdu.edu.cn/showproblem.php?pid=3909 最全的经典数独题 N = 2,3,4

  Squiggly Sudoku http://acm.hdu.edu.cn/showproblem.php?pid=4069 数独变形 + DFS找连通块


  题意:对于一个N阶的数独,由N^2×N^2个格子组成,如图7-1为一个3阶的数独。要求满足四个限制条件:

    1) 每个格子只能填1个数;

    2) 每行的数字集合为[1, N^2],且不能重复;

    3) 每列的数字集合为[1, N^2],且不能重复;

    4) 每个“宫”的数字集合为[1, N^2],且不能重复;

图7-1

  其中“宫”的意思就是N×N的格子。对于N=3的情况,就是“九宫格”。现在问题是给定一个已经填了一些数字的数独,求当N=3时的一种解,满足以上四个限制条件。

  题解:转变为精确覆盖问题。行代表问题的所有情况,列代表问题的约束条件。

  每个格子能够填的数字为[1, 9],并且总共有3^2×3^2个格子,所以总的情况数为729种。也就是Dancing Links的行为729行。

  列则分为四种:

  1) [0,81)列       分别对应了81个格子是否被放置了数字。

  2) [82,2*81)列     分别对应了9行,每行[1, 9]个数字的放置情况;

  3) [2*81,3*81)列    分别对应了9列,每列[1, 9]个数字的放置情况;

  4) [3*81,4*81)列    分别对应了9个“宫”,每“宫”[1, 9]个数字的放置情况;

  所以总的列数为4*81=324列。

图7-2

  举个例子,对于在数独棋盘的i行j列的格子(i, j)上放置一个数字k,那么对应的Dancing Links的01矩阵行,一行上有四个“1”,分别对应四种约束条件:

  1) 格子限制:行号*9+列号

  2) 行不重复限制:81 + 行号*9 + (k-1)

  3) 列不重复限制:2*81 + 列号*9 + (k-1)

  4) “宫”不重复限制:3*81 + 宫号*9 + (k-1)

  行号是i,列号是j,比较好理解;那么宫号我们定义如下图:

图7-3

  宫号的计算方式可以通过行号和列号得出。即 宫号 = (i/3)*3 + (j/3);

  那么构建01矩阵的时候,我们从上到下,从左到右遍历数独,对于在(i, j)上有数字k的只需要插入一行,这行上有四列为“1”。对于没有填写数字的需要枚举[1, 9],把在(i, j)位置上填[1, 9]的情况都进行插入,一共9行。

  矩阵构建完毕,求一次精确覆盖即可。


二、重复覆盖


8) whosyourdaddy

  题意:给定一个N(N<= 50)个结点M条边的无向图,结点i选的话,所有和i相连的点都能被选中,求找出一个最少的点集合,使得所有点都能被选中。

  题解:经典重复覆盖问题。利用IDA* + Dancing Links 求解。

  重复覆盖的问题描述为:对于一个01矩阵,选出最少的行集合满足每列上至少一个“1”。

  建立Dancing Links矩阵matrix[i][j] = 1当且仅当i和j相连或i=j。重复覆盖和精确覆盖稍微有点区别,即每次删除一列的时候,不能将这列上为“1”的行删除(这个是显然的,因为是重复覆盖,所以取出的行集合的一列上允许有多个“1”)。

  具体步骤如下:

    枚举递归最大深度maxDepth = [0, n)

      利用DLX求解,如果有解 or 递归深度depth超过maxDepth则返回;

  DLX求解时,每次找结点数最少的列mc,枚举mc的每一行被选中的情况。令本次选中的行为r,将r加入部分解,并将行r上有“1”的列全部删除。然后递归求解子矩阵。

  这里需要注意的是,在删除列的时候其实只是将对应列的列首结点从链表中删除,所以在枚举行r上有“1”的列时,会枚举到已经删除的列,所以需要记录一个标记col_covered[i] 来记录第i列被删除了几次,这个是很重要的,因为在回溯的时候需要进行相应的恢复,也就是说删除列的算法需要这么写:

  DoDeleteColumni

    If(col_covered[i] is ZERO)

        DeleteColumn i

    ++ col_covered[i]

  那么在相应的回溯矩阵时,对删除的列进行恢复时,也要按照相反顺序恢复,即:

  DoResumeColumni

    -- col_covered[i]

    If(col_covered[i] is ZERO)

        Delete Column i

  这样才能保证列删除的正确性。这里的概念等同于引用计数,即一列并不是“被删除”和“未被删除”两种状态,而是有“被删除0次”、“被删除1次”、“被删除2次”、“被删除N次”多种状态组成。

  最后,由于重复覆盖的矩阵下降程度远远小于精确覆盖,会导致搜索空间树呈指数级增长的速度远远大于精确覆盖的情况,所以需要剪枝。这里介绍一种启发性函数(估价函数),也就是迭代加深(IDA*)里面的那个A*。

  考虑到当前枚举深度为depth,最大枚举深度maxDepth,也就是当前搜索条件下只能最多再选择maxDepth – depth行。那么如果我们可以设计一个估价函数H()。函数返回的是剩余列删除需要选择最少的行个数。如果H() >maxDepth – depth,代表当前搜索条件下已经不可能搜到可行解了,可以直接返回,此所谓“剪枝”。

  H()函数的计算原理:模拟每一列的删除,估算需要选择的行数。由于总的列数小于64,所以我们可以将每一行压缩成一个64位的整数,R[i]表示第i行的那个64位整数。用一个全局标记X记录剩下列的模拟删除情况(X的二进制第i位为“1”代表第i列已经被模拟删除)然后枚举剩下的列,如果这列没有被模拟删除,那么将它列上有“1”的行全部模拟选中,计数器cnt+1,行i模拟选中的意思就是将X = X or R[i];循环遍历每一列,最后cnt就是返回值。

  通过这种方法计算出来的为估计代价,一定是小于等于实际代价的。

  最后,IDA*的maxDepth就是问题的解。


9) Radar

  题意:N个城市和M个雷达,每个雷达的范围为R,雷达和城市之间的距离为欧几里得距离,当距离小于等于R时能被覆盖到,求一个最小的距离R,使得所有的城市都能被覆盖到(N,M <= 50)。

  题解:二分 + 重复覆盖。利用IDA* + Dancing Links 求解。

  因为距离R满足单调性,即当R越大,城市被雷达覆盖的概率越大,所以可以采用二分。首先二分答案R,建立雷达和城市之间的覆盖关系图,然后利用IDA* + Dancing Links,如果能够求出一个重复覆盖,则减小R的值,反之,增大R。

  注意枚举R的时候可以用整型,判断距离的时候采用平方,减小误差。


10) Bomberman - Just Search!

  题意:给定一个N*M(N,M<=15)的图,其中'#'代表可以被炸弹炸裂的墙;‘.’代表可以放置炸弹的地方;‘*’代表不能被炸弹炸裂的墙;求放置最少的炸弹,将所有的‘#’炸裂。

  题解:重复覆盖。

  建图。给每个‘.’编号(r1, r2, … rn),给每个‘#’编号(c1, c2, … cm)。那么可以建立一个n×m的矩阵A,A[i][j] = 1代表 ri这个位置放置的炸弹可以炸到cj,枚举构建这个矩阵,然后求一次重复覆盖即可。


11) Repair Depots

  题意:给定n(n<= 16)个机器人的位置,求建立c(1 <= c <= n)个修理厂,每个机器人选择最近的修理厂进行修理,机器人i和最近的那个修理厂的距离为d[i],现在要求max{d[i] | 1<=i<=n}最小,求这个最小值。

  题解:Radar的加强版,难点在于修理厂的位置没有给出,我们需要模拟放置修理厂。修理厂可以放在三种位置上:

1、任意一个机器人的位置上

2、任意两个机器人的中心

3、任意三个机器人的外心(三角形外切圆圆心)

  这样总的修理厂的可行个数是n + n*(n-1)/2 + n*(n-1)*(n-2)/6,也就是对应了Dancing Links矩阵的行,列就是n。

  然后二分距离R,继续小于等于R的修理厂和机器人之间连边。

  这里有一个很明显的优化,就是“子集剔除”。当某个修理厂的位置能够覆盖到1、3、6号机器人时,某些只能覆盖到这个机器人集合的子集的修理厂可以不需要。这个很好理解,因为是重复覆盖,一个01矩阵的任意两行,如果行A完全包含行B,那么行B一定不会比行A更优,行B可以直接剔除。

  “子集剔除”可以利用dfs+ hash从大到小枚举在O(2^n)的时间内完成。


12) Fire station

  题意:给定n(n<= 50)个房子,需要在这些房子上建造m(1 <= m <= n)个消防站,第i个房子到最近的消防站的距离为d[i],现在要求max{d[i] | 1<=i<=n}最小,求这个最小值。

  题解:Radar的数据加强版。二分距离 + 重复覆盖。

  这里需要做一个“子集剔除”,否则会超时。由于矩阵的行数只有50,所以可以直接O(n^2)的枚举行i是否包含于行j,如果某一行被另一行包含,那么这行可以剔除。

  由于列数也是50,所以每一行的状态可以压缩成一个INT64的整型。即第i的行状态可以用state[i]表示。那么第i行被第j行包含的判断条件为:state[i] != state[j] 并且 (state[i] & state[j]) == state[i]。

  而且由于精度问题,二分距离的时候需要精确到1e-8。

 

13) Street Fighter

  题意:N(N <= 25)个角色有M(M <= 2)种不同形态,给定每种形态下能够打败的其它角色相应形态的列表。求一个最小的角色列表,使得这个列表里面的角色能够打败列表以外的其它角色。

  题解:精确覆盖 + 重复覆盖。构建DancingLinks矩阵A时,2N行代表N个角色的M种形态,2N列代表被打败的角色信息。

  如果R1(0 <= R1<N)号角色的M1(0 <= M1 < M)号形态可以打败R2号角色的M2号形态,则:

    A[R1*2 + M1][R2*2 + M2] = 1;

  题目要求选择一个角色时只能选取其中的一种形态,也就是说如果0号形态用了,那么1号形态就不能用了。这个对应了精确覆盖,所以在DLX矩阵的最后N列还要给出每个形态对应的角色。

  这里还有一个隐含条件:就是任何角色的任何形态一定能够打败自己的任何形态。这样就可以开始求解了,前2N列求重复覆盖,后N列求精确覆盖。

 

14) 神龙的难题

  题意:给定一个N×M的01矩阵,(N, M<= 15),再给定X×Y的框,要求用X×Y的框去覆盖这个01矩阵,使得所有的1都能被覆盖住,求满足条件的最少的框个数。

  题解:重复覆盖。01矩阵中的每个1代表DLX矩阵中的一列,X×Y的框的所有放置情况代表DLX矩阵中的行,建立DLX关系矩阵求重复覆盖即可。

  注意,这里的列数可能超过64,所以进行行状态hash的时候每行不能用一个INT64来表示,而是要用一个INT64的数组来表示状态。


15) Square Destroyer

  题意:给定如图所示n*n(n <=5)的正方形,由2n(n+1)根火柴组成,求去掉最少的火柴使得所有的正方形都不存在。

图15-1

  题解:经典重复覆盖问题。模拟计算每根火柴能够销毁那些正方形。火柴对应Dancing Links 矩阵的行,正方形对应矩阵的列, A[i][j] = 1当且仅当第i根火柴是第j个正方形的组成部分(也可以理解成第i根火柴拿掉,第j个正方形就摧毁了)。建好图后求一次重复覆盖即可。


16) Airport

  题意:Radar的变形。唯一的不同是把距离的计算方式从欧几里得距离变成了曼哈顿距离。二分距离,建图后计算重复覆盖即可。


17) A simple math problem.

  题意:一个买彩票的问题。N个数字选M个,如果中了R个数就算中二等奖,问至少要买多少张彩票才能使得至少中一次二等奖(1<=R<=N<=M<=8)。

  题解:C[N][M]为总共N个数选M个的情况数,C[N][R]为N个数选R个的情况数。那么建立DLX矩阵,矩阵的行代表选择数字的所有情况,矩阵的列代表所有中奖的情况,那么行数为C[N][M],列数为C[N][R]。

  如图17-1,代表N=4,M=3,R=2的情况,每一行代表了彩票的购买情况(为了加以区分,四种情况分别采用四种颜色表示)。在每种情况下,能够中奖的情况用彩色格子进行标记。那么问题转化成了重复覆盖问题。

  N=8,M=5,R=3的情况,搜索空间巨大,DancingLinks搜索半天出不来,最后只能采用流氓法打表了。

图17-1


标签:01,题意,覆盖,Dancing,题解,矩阵,Links,解题,代表
来源: https://blog.51cto.com/u_15239535/2849762

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有