2.分治法求解棋盘问题

一、问题:对于一个 2^k * 2^k (k>=0) 的棋盘,恰有一个方格(下称为特殊方格)与其他不同,请用如下4种L型骨牌将除了特殊方格以外的格子覆盖,骨牌间不可重复覆盖

例题:                  答案
----------------       ----------------
|   |   |   |   |      | 1 | 1 | 2 | 2 | 
+---+---+---+---+      +---+---+---+---+   
|   | X |   |   |      | 1 | X | 3 | 2 | 
+---+---+---+---+      +---+---+---+---+
|   |   |   |   |      | 4 | 3 | 3 | 5 | 
+---+---+---+---+      +---+---+---+---+
|   |   |   |   |      | 4 | 4 | 5 | 5 |
+---+---+---+---+      +---+---+---+---+

X是特殊方格,1~5是骨牌编号。

编号可以随意排序


 

二、思路:

通过分析题目我们可以发现,由于用于覆盖格子的L型骨牌只能覆盖三格,

所以对于任意2*2的格子,总有一个无法被骨牌覆盖。对于这个4*4的格子来说无疑是特殊格子

 

但是对于题目来说呢?很明显,题目只有一个特殊的,无法被覆盖的格子啊,那这些所谓的特殊的格子是从哪里冒出来的呢?

很显然是属于别的骨牌的一部分嘛,你这个骨牌够不到的地方,不代表别的骨牌覆盖不了啊。

 

既然如此,我们将那些骨牌还原一下,让那些无法被覆盖的格子组成一个完整的骨牌好了。

于是我们得到下图(其中一种情况)。可以看到,有三个红色的格子组成一个完整的骨牌了:

 

嗯?绿色那块是怎么回事呢,怎么没有被拼成其他骨牌?

绿色那块对于格子G来说就是特殊格子嘛!

 

推而广之:绿色格子有没有可能是别的骨牌的一部分?很有可能:

我们可以这样一路向上分析(当然对于格子B来说不可能是其他骨牌的一部分了),可以推导出16*16,32*32的棋盘的情况。

 

这样,对于这个题目已经自底向上分析一波了

只要逆过来,自顶向下分析,就能得到本题的解法

刚才我们分析的时候是组成骨牌,反过来不就变成了拆散骨牌了嘛:

1、对于题目给出的棋盘G,平分为四块分棋盘。含特殊格子的分棋盘为G1(一个),不含特殊格子的为G2,G3,G4

2、用一个骨牌X覆盖棋盘,且被覆盖的格子X1,X2,X3分别在G2,G3,G4上

G1的特殊格子为题目的特殊格子;G2,G3,G4的特殊格子则为被骨牌X覆盖的X1,X2,X3

这样每个分棋盘都有特殊格子啦!

对于每个分棋盘G1,G2,G3,G4再次重复如上的分割方式,直到分割成2*2的小格子,分无可分为止。

对于分无可分的格子,我们给它盖上骨牌就行了。

当然,题目棋盘大小可能为1*1,到时候做些判断就行了

 

看!分治法在这道题目中的应用就是将一个大棋盘分割成4个都含有特殊格子的小棋盘(分),

等到分无可分时给棋盘盖上骨牌就行了(治)

 

代码:

#include<stdio.h>

#define maxn 4

int board[maxn][maxn];
int t;

void ChessBoard(int tr,int tc,int dr,int dc,int size){
	int s,t1;
	if(size==1)return;
	t1=++t;
	s=size/2;

	if(dr<tr+s && dc<tc+s){
		ChessBoard(tr,tc,dr,dc,s);
	}else{
		board[tr+s-1][tc+s-1]=t1;
		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]=t1;
		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]=t1;
		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]=t1;
		ChessBoard(tr+s,tc+s,tr+s,tc+s,s);
	}
}

int main(){
	t=0;
	ChessBoard(0,0,3,2,4);
	for(int i=0;i<maxn;++i){
		for(int j=0;j<maxn;++j){
			printf("%d ",board[i][j]);
		}
		printf("\n");
	}
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值