问题描述
在一个2^k×2^k (k≥0)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格。显然,特殊方格在棋盘中可能出现的位置有4^k种,因而有4^k种不同的棋盘,图4.10(a)所示是k=2时16种棋盘中的一个。棋盘覆盖问题(chess cover problem)要求用图4.10(b)所示的4种不同形状的L型骨牌覆盖给定棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
解题思路
分治的技巧在于如何划分棋盘,使划分后的子棋盘的大小相同,并且每个子棋盘均包含一个特殊方格,从而将原问题分解为规模较小的棋盘覆盖问题。
k>0时,可将2^k×2^k的棋盘划分为4个2^(k-1)×2^(k-1)的子棋盘,如图4.11(a)所示。这样划分后,由于原棋盘只有一个特殊方格,所以,这4个子棋盘中只有一个子棋盘包含该特殊方格,其余3个子棋盘中没有特殊方格。
为了将这3个没有特殊方格的子棋盘转化为特殊棋盘,以便采用递归方法求解,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如图4.11(b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种划分策略,直至将棋盘分割为1×1的子棋盘。
实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
int count = 0;
int matr[20][20];//棋盘
//(tr,tc)表示棋盘的起始位置
//(dr,dc)表示特殊点的位置
//size表示棋盘的大小
void chessBox(int tr,int tc,int dr,int dc,int size){
if(size == 1){//当子棋盘大小为1时退出
return ;
}
int s = size/2;//将棋盘分成4个子棋盘
int t = ++count;
if(dr<tr+s && dc<tc+s){//当特殊点在左上角的子棋盘上
chessBox(tr,tc,dr,dc,s);
}else{//当特殊点不在左上角的子棋盘上
matr[tr+s-1][tc+s-1] = t;//将左上角的子棋盘的右下角的方格变成 特殊点
chessBox(tr,tc,tr+s-1,tc+s-1,s);//递归搜索此棋盘
}
if(dr<tr+s && dc>=tc+s){//当特殊点在右上角的子棋盘上
chessBox(tr,tc+s,dr,dc,s);
}else{//当特殊点不在右上角的子棋盘上
matr[tr+s-1][tc+s] = t;//将右上角的子棋盘的左下角的方格变成 特殊点
chessBox(tr,tc+s,tr+s-1,tc+s,s);//递归搜索此棋盘
}
if(dr>=tr+s && dc<tc+s){//当特殊点在左下角的子棋盘上
chessBox(tr+s,tc,dr,dc,s);
}else{//当特殊点不在左下角的子棋盘上
matr[tr+s][tc+s-1] = t;//将左下角的子棋盘的右上角的方格变成 特殊点
chessBox(tr+s,tc,tr+s,tc+s-1,s);//递归搜索此棋盘
}
if(dr>=tr+s && dc>=tc+s){//当特殊点在右下角的子棋盘上
chessBox(tr+s,tc+s,dr,dc,s);
}else{//当特殊点不在右下角的子棋盘上
matr[tr+s][tc+s] = t;//将右下角的子棋盘的左上角的方格变成 特殊点
chessBox(tr+s,tc+s,tr+s,tc+s,s);//递归搜索此棋盘
}
}
int main(){
int size;//棋盘初始长度
int row,col;//特殊点的坐标
memset(matr,0,sizeof(matr));//将棋盘的所有值都赋值为0
cout<<"请输入棋盘长度:";
cin>>size;
cout<<"请输入特殊点坐标:";
cin>>row>>col;
chessBox(0,0,row,col,size);
for(int i = 0;i<size;i++){
for(int j = 0;j<size;j++){
cout<<matr[i][j]<<" ";
}
cout<<endl;
}
}