递归之棋盘覆盖问题

问题描述:在一个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 );
	}
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值