棋盘覆盖问题
划分:将 2k∗2k 的棋盘划分为 2k−1∗2k−1 这样的子棋盘4块。
求解:递归填充各个格子,填充分为四个情况(见后文),递归出口为k=0也就是子棋盘方格数为1。
合并:不需要合并子问题。
解决方案
就是利用分治法,将方形棋盘分成4部分,如果该特殊点在某一部分,我们就去递归他,如果不在某一部分,我们假设一个点为特殊点,同样递归下去,直到全覆盖。
左上角的子棋盘(若不存在特殊方格):则将该子棋盘右下角的那个方格假设为特殊方格;
右上角的子棋盘(若不存在特殊方格):则将该子棋盘左下角的那个方格假设为特殊方格;
左下角的子棋盘(若不存在特殊方格):则将该子棋盘右上角的那个方格假设为特殊方格;
右下角的子棋盘(若不存在特殊方格):则将该子棋盘左上角的那个方格假设为特殊方格;
数据结构
- 棋盘 —— 使用二维数组表示;
int board[size][size];
,令 size = 2k ,表示棋盘的规格。为方便调用,将board设为全局变量,board[0][0]是左上角方格。 - 子棋盘 —— 由棋盘左上角的坐标tr,tc和棋盘大小s表示。
- 特殊方格 —— 坐标位置为(dr,dc)。
- L型骨牌 —— 用到L型骨牌个数为
(4k−1)/3
,将所有骨牌从1编号,用全局变量表示。
static int tile = 1;
代码实现
#include
#include
using namespace std;
int board[1025][1025]; // 棋盘
static int tile = 1; // 骨牌编号
/**
* (tr,tc) : tr棋盘左上角的行号,tc棋盘左上角的列号
* (dr,dc) : dr特殊方格左上角的行号,dc特殊方格左上角的列号
* size :size = 2^k 棋盘规格为2^k*2^k
*/
void ChessBoard(int tr,int tc,int dr,int dc,int size)
{
if(size==1) return ; // 递归边界
int t=tile++; // L型骨牌编号
int s=size/2; // 分割棋盘
// 覆盖左上角子棋盘
if(dr
=tc+s) // 特殊方格在此棋盘中
ChessBoard(tr,tc+s,dr,dc,s);
else
{
// 用编号为t的骨牌覆盖左下角
board[tr+s-1][tc+s]=t;
ChessBoard(tr,tc+s,tr+s-1,tc+s,s);
}
// 覆盖左下角子棋盘
if(dr>=tr+s && dc
=tr+s && dc>=tc+s)
ChessBoard(tr+s,tc+s,dr,dc,s);
else
{
// 用编号为t的骨牌覆盖左上角
board[tr+s][tc+s]=t;
ChessBoard(tr+s,tc+s,tr+s,tc+s,s);
}
}
int main()
{
int i,j;
int k;
while(cin>>k)
{
int size = 1<
>x>>y; board[x][y]=0; ChessBoard(0, 0, x, y, size); for(i=0; i
时间复杂度分析
设T(k)为覆盖棋盘的时间。从分治策略可知递推式:
T(k)={O(1),4T(k−1)+O(1),k=0k>0
解可得
T(k)=O(4k)
。(
n=2k,n2=4k
)。这是一个渐进意义下的最优算法。