概述
本文承接上文《求n个元素的全排序(从分治思想超详细解析)》,将再用一个详细的案例来带我们更深入的理解分治思想在解决实际问题中的体现。
同时,在后文我会把将前文的知识整合起来写成《算法分析与设计实验》的实验报告2–递归与分治策略应用基础,分享给大家。同为大学狗的可以借鉴一下👴的微薄之作
L型骨牌覆盖问题
1.问题说明
在一个 2^k * 2^k 方格组成的棋盘中,若恰有一个方格与其他方格不同,称该方格为特殊方格,且称该棋盘为特殊棋盘(Defective Chessboard)。下图的特殊棋盘是当k=2 时16个特殊棋盘中的一个,在棋盘覆盖问题中,要求用图示所示的4种不同形状的L型骨牌覆盖给定棋盘上除特殊方格以外的所有方格,且任何俩个L型骨牌方格不得重叠。在任何一个2k*2k 的覆盖棋盘中,用到的L型骨牌个数为(4^k-1)/3。
2.算法分析
同样,我们可以把这个问题利用分治思想拆分成一个一个的小问题。
分治的技巧在于如何划分棋盘,使划分后的子棋盘大小相同,并且每一个子棋盘均包含一个特殊方格,从而将原问题分解为规模较小的棋盘覆盖问题。
当k>0时,将2k * 2k的棋盘划分为4个2(k-1)*2(k-1)子棋盘,我们可以推断出原棋盘只有一个特殊方格,则其余三个子棋盘中没有特殊方格。
由于我们是L型骨牌,所以这三个没有特殊方格的子棋盘的结合处我们可以正好用L型骨牌覆盖,一旦覆盖,他们在各自的子棋盘中又会变成棋盘覆盖问题,不过规模变小了,之后我们就可以逐步递归求解,直至将棋盘分割为1*1 的子棋盘。
这里解释一下结合处,比如左上棋盘的结合处就是右下角,右上棋盘的结合处就是左下角,反正就是对立的
由此,我们得出核心的算法思路:
- 把大棋盘先分成4个小棋盘
- 依次判断每个小棋盘里有没有特殊方格,如果没有,将结合处用L型骨牌覆盖上,注意这时候一旦覆盖说明在这个小棋盘中有了一个特殊方格(就是刚刚被覆盖掉的那一个格子),我们需要递归。如果有,我们直接递归寻解。
- 一旦棋盘分割为1*1 的子棋盘表明递归终止。
3.代码实现
//L型骨牌从1开始连续编号
public static int number = 1;
public static int[][] board;//整形数组保存棋盘
public LshapeCover(int size) {
this.board = new int[size][size];//size--棋盘的规格
}
/**
* 棋盘覆盖函数
* @param tr 标示当前所在棋盘的左上角行位置
* @param tc 标示当前所在棋盘的左上角列位置
* @param dr 标示特殊方格的行位置
* @param dc 标示特殊方格的列位置
* @param size
*/
public static void chessBorad(