问题描述:在一个2^k * 2 ^k 个方格组成的棋盘中,若有一个方格与其他方格不同,则称这个方格为一个特殊方格,且该棋盘为一个特殊棋盘。需要用下面四种不同形态的L型骨牌覆盖一个给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不能重复覆盖。请给出一个覆盖方案。
显然,特殊方格在棋盘上出现的位置有4^k种情况。容易得到,用到的L型骨牌个数恰好是(4^k - 1)/ 3。我们用分治的策略,可以设计一个解决这个问题的简捷算法。
(a) (b)
特殊方格必定位于4个较小的棋盘之一,其余的3个子棋盘中无特殊方格。为了将这3个无特殊方格的字棋盘化为特殊棋盘,我们可以用一个L型骨牌覆盖着3个棋盘的会合处,如(b),这3个子棋盘上被L型棋盘覆盖的方格就成为该棋盘上的特殊方格,从而将原问题转化为4个规模较小的棋盘覆盖问题。递归使用这种分割,直至棋盘简化为1*1棋盘。
下面上代码,在代码中解释程序
/*
tr:棋盘左上角方格行号
tc:棋盘左上角方格列好号
dr:特殊方格所在列号
dc:特殊方格所在列号
size:size = 2 ^ k, 棋盘规模为2 ^ k * 2 ^ k;
*/
#include <iostream>
using namespace std;
int tile = 0;//L型骨牌的牌号
int Board[ 4 ][ 4 ];//一个4*4的方格,如果你愿意,可以弄成其他规模的,但要满足size = 2 ^ k;
void ChessBoard( int tr, int tc, int dr, int dc, int size );
int main(){
Board[ 0 ][ 0 ] = 100;
ChessBoard( 0,0,0,0, 4 );
for( int i = 0; i < 4; ++i ){
for( int j = 0; j < 4; ++j )
printf( "%-5d", Board[i][j] );
cout << endl;
}
}
void ChessBoard( int tr, int tc, int dr, int dc, int size ){
if( size == 1 )return;
int t = tile++;//L型骨牌的牌号
int s = size / 2;//分割棋盘,分成4个小部分,每个小部分规模是 s * s;
//覆盖左上角子棋盘
if( dr < tr + s && dc < tc + s ){
//特殊方格在此棋盘,递归调用
ChessBoard( tr, tc, dr, dc, s );
}else {
//特殊方格不在这个子棋盘里
//用t号L型骨牌覆盖右下角
//为什么选择右下角呢?因为我们要覆盖子棋盘的会合处啊,左上角子棋盘与其他子棋盘的会合处明显就是在该棋盘右下角嘛
Board[ tr + s - 1 ][ tc + s - 1 ] = t;
//该棋盘右下角被覆盖后,将该棋盘右下角当做特殊方格,进行递归
//tr,tc变为左上角子棋盘的起始位置(这里其实没变),dr,dc变成该棋盘右下角的坐标
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型骨牌覆盖左下角
//为什么选择左下角呢?因为我们要覆盖子棋盘的会合处啊,右上角子棋盘与其他子棋盘的会合处明显就是在该棋盘的左下角嘛
Board[ tr + s - 1 ][ tc + s ] = t;
//该棋盘的左下角被覆盖后,将该棋盘的左下角当做特殊方格,进行递归
//tr,tc变为右上角子棋盘的起始位置,dr,dc变成该棋盘的左下角的坐标
ChessBoard( tr, tc + s, tr + s - 1, tc + s, s );
}
//覆盖左下角子棋盘
if( dr >= tr + s && dc < tc + s ){
//特殊方格在此棋盘,递归调用
ChessBoard( tr + s - 1, tc, dr, dc, s );
}else{
//特殊方格不在这个子棋盘里
//用t号L型骨牌覆盖右上角
//为什么选择右上角呢?因为我们要覆盖子棋盘的会合处啊,左下角子棋盘与其他子棋盘的会合处明显就是在该棋盘的右上角嘛
Board[ tr + s ][ tc + s -1 ] = t;
//该棋盘的右上角被覆盖后,将该棋盘的右上角当做特殊方格,进行递归
//tr,tc变为左下角子棋盘的起始位置,dr,dc变成该棋盘的右上角的坐标
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型骨牌覆盖右上角
//为什么选择左上角呢?因为我们要覆盖子棋盘的会合处啊,右下角子棋盘与其他子棋盘的会合处明显就是在该棋盘的左上角嘛
Board[ tr + s ][ tc + s ] = t;
//该棋盘的左上角被覆盖后,将该棋盘的左上角当做特殊方格,进行递归
//tr,tc变为右下角子棋盘的起始位置,dr,dc变成该棋盘的左上角的坐标
ChessBoard( tr + s, tc + s, tr + s, tc + s, s );
}
}