一个2k*2k的棋盘,指定一点为特殊方格(无需覆盖),然后用(4k-1)/3个L型骨牌无重叠地覆盖其余所有方格。下图为一个示例:
将一个规模为n问题分为k个规模较小的子问题,这些子问题互相独立且与原问题相同,递归或迭代地解这些子问题,再将子问题合并得到原问题的解。
以一个实例说明棋盘覆盖问题的分治思想。
上图为 2 3 ∗ 2 3 2^3*2^3 23∗23的棋盘,黑色方格为指定特殊方格。
第一次分割后形成4个 2 2 ∗ 2 2 2^2*2^2 22∗22的棋盘,需要为没有特殊方格的棋盘指定特殊方格(保证子问题互相独立且与原问题相同),即黄色的1部分(这样指定刚好是一个L型骨牌)。
第二次分割以右上角 2 2 ∗ 2 2 2^2*2^2 22∗22棋盘为例,分割后形成4个2*2的棋盘,同样为没有特殊方格的棋盘指定特殊方格(白色的2部分)。
最终,2*2的棋盘再覆盖一次即可完成。在指定特殊方块的过程中,问题规模也越来越小,依次完成上左、上右、下左、下右四个期棋盘的覆盖(顺序可任意规定)后,大棋盘的覆盖也就随之完成。
代码实现可分为三步:分割棋盘(规模缩小一倍),判定特殊方格的位置(将特殊方格的坐标与棋盘中间坐标对比)、根据比较结果决定对应棋盘用不用增添特殊方格。
T ( k ) = { O ( 1 ) , k=0 4 T ( k − 1 ) + O ( 1 ) , k>1 T(k) = \begin{cases} O(1), & \text{k=0} \\ 4T(k-1)+O(1),&\text{k>1} \end{cases} T(k)={ O(1),4T(k−1)+O(1),k=0k>1
#include
int tile=1; //L型骨牌序号
int board[128][128]; //二维数组模拟棋盘
//(tr,tc)表示棋盘的左上角坐标(即确定棋盘位置),size=2^k确定棋盘大小;(tc,dc)表示特殊方块的位置
void chessBoard(int tr,int tc,int dr,int dc,int size){
//递归出口
if(size==1)
return;
//分割棋盘
int s=size/2;
int t=tile++; //t记录本层骨牌序号
//判断特殊方格在不在左上棋盘
if(dr<tr+s&&dc<tc+s){
chessBoard(tr,tc,dr,dc,s); //特殊方格在左上棋盘的递归处理方法
}else{
board[tr+s-1][tc+s-1]=t; //不在左上棋盘的话,其特殊方块位于中点(tr+s,tc+s)的左上方
chessBoard(tr,tc,tr+s-1,tc+s-1,s); //特殊方格不在左上棋盘的递归处理方法
}
//判断特殊方格在不在右上棋盘
if(dr<tr+s&&dc>=tc+s){
chessBoard(tr,tc+s,dr,dc,s);
}else{
board[tr+s-1][tc+s]=t;
chessBoard(tr,tc+s,tr+s-1,tc+s,s);
}
//判断特殊方格在不在左下棋盘
if(dr>=tr+s&&dc<tc+s){
chessBoard(tr+s,tc,dr,dc,s);
}else{
board[tr+s][tc+s-1]=t;
chessBoard(tr+s,tc,tr+s,tc+s-1,s);
}
//判断特殊方格在不在右下棋盘
if(dr>=tr+s&&dc>=tc+s){
chessBoard(tr+s,tc+s,dr,dc,s);
}else{
board[tr+s][tc+s]=t;
chessBoard(tr+s,tc+s,tr+s,tc+s,s);
}
}
int main(){
chessBoard(0,0,5,5,8); //(0,0)为顶点,大小为8的棋盘; 特殊方块位于(5,5)
int i,j;
printf("\n\n\n");
for(i=0;i<8;i++){
//输出棋盘内容
for(j=0;j<8;j++){
printf("%d\t",board[i][j]);
}
printf("\n\n\n");
}
return 0;
}