棋盘覆盖分析与实现
一、什么是棋盘覆盖?
在一个 2^k * 2^k 个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为一个特殊方格,且称该棋盘为一个特殊棋盘。显然,特殊方格在棋盘上出现的位置有 4^k 种情况,即k>=0,有4^k种不同的特殊棋盘。
棋盘覆盖:用4种不同形态的L型骨牌覆盖一个给定的特殊棋盘(即特殊方格的位置已经确定了)上除特殊方格外的所有方格,且任何两个L型骨牌不得重复覆盖。按照规则,我们很容易知道,在2^k*2^k的棋盘覆盖中,用到的L型骨盘数恰为(4^k-1)/3,即(所有方格个数-特殊方格个数)/3。
如下图,为k=2时的一个特殊棋盘(相同颜色的三个小方格组成一个L型骨牌)和4种不同形态的L型骨牌,蓝色的为特殊方格:
k=2时的一个特殊棋盘
二、实现棋盘覆盖的思路和方法
棋盘覆盖实现的基本方法为分治法,是设计棋盘覆盖的一个简捷的算法,那什么是分治法?
分治法的基本思路:将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归地解决这些子问题,然后将各个子问题的解合并得到原问题的解。简单地说,就是将规模为n的问题自顶向下分解,直到子问题分解到足够小,可以容易解决时,再自底向上合并,从而得到原来的解。
分析思路:当k>0时,将2^k*2^k棋盘分割为4个2^(k-1)*2^(k-1)子棋盘,如图所示:
特殊方格必定位于这四个小棋盘中,其余三个子棋盘没有特殊方格,为了将这三个无特殊方格的子棋盘转换为特殊棋盘,我们可以用一个L型骨盘覆盖这三个较小棋盘的会合处,如图所示:
从图上可以看出,这三个子棋盘上被L型骨牌覆盖的方格就成为该棋盘上的特殊方格,从而将问题分解为4个较小规模的棋盘覆盖问题。递归地使用这种分割方法,直至棋盘简化为1*1棋盘,就结束递归。
实现这种算法的分析:每次都对分割后的四个小方块进行判断,判断特殊方格是否在里面。这里的判断的方法是每次先记录下整个大方块的左上角方格的行列坐标,然后再与特殊方格坐标进行比较,就可以知道特殊方格是否在该块中。如果特殊方块在里面,这直接递归下去求即可,如果不在,这根据分割的四个方块的不同位置,把右下角、左下角、右上角或者左上角的方格标记为特殊方块,然后继续递归。在递归函数里,还要有一个变量s来记录边的方格数,每次对方块进行划分时,边的方格数都会减半,这个变量是为了方便判断特殊方格的位置。
三、棋盘覆盖的具体实现代码
#include <stdio.h>
#include <stdlib.h>
int num = 0;
int Matrix[100][100];
void chessBoard(int tr, int tc, int dr, int dc, int size);
int main()
{
int size,r,c,row,col;
printf("请输入棋盘的行列号");
scanf("%d",&size);
printf("请输入特殊方格的行列号");
scanf("%d %d",&row,&col);
chessBoard(0,0,row,col,size);
for (r = 0; r < size; r++)
{
for (c = 0; c < size; c++)
{
printf("%2d ",Matrix[r][c]);
}
printf("\n");
}
return 0;
}
void chessBoard(int tr, int tc, int dr, int dc, int size)
{
if (size==1)
return;
int s = size/2; //分割棋盘
int t = ++num; //L型骨牌号
//覆盖左上角子棋盘
if (dr < tr + s && dc < tc +s)
{
//特殊方格在此棋盘中
chessBoard(tr,tc,dr,dc,s);
}
else //此棋盘中无特殊方格
{
//用t号L型骨牌覆盖右下角
Matrix[tr+s-1][tc+s-1] = t;
//覆盖其余方格
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 //此棋盘中无特殊方格
{
//用t号L型骨牌覆盖左下角
Matrix[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
{
//用t号L型骨牌覆盖右上角
Matrix[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
{
//用t号L型骨牌覆盖左上角
Matrix[tr+s][tc+s] = t;
//覆盖其余方格
chessBoard(tr+s,tc+s,tr+s,tc+s,s);
}
}
运行结果如下图:
上述算法中用一个二维整型数组 Matrix 表示棋盘,Matrix[0][0]是棋盘的左上角方格。num 是一个全局整型变量,用来表示 L 型骨牌的编号,其初始值为1。算法的输入参数是:
tr | 棋盘左上角方格的行号 |
---|---|
tc | 棋盘左上角方格的列号 |
dr | 特殊方格的行号 |
dc | 特殊方格的列号 |
size | size=2^k,棋盘规格为 2^k * 2^k |
Reference:
1、https://www.cnblogs.com/crx234/p/5988055.html
2、计算机算法设计与分析王晓东第五版。(如果需要对应课后习题答案的话可以评论区留言,我会发链接哒)