棋盘覆盖问题详解(递归,含有代码)
(代码由 java 编写)
1.问题描述
如图(a)所示,k>0时,有一个2k×2k的棋盘,棋盘中任意位置有一个特殊的方格,要求利用图(b)中的4中L型骨牌覆盖图(a)除特殊方格以外的区域,要求所有区域都要覆盖,并且骨牌不能重叠。
2.解题思路
可将2k×2k的棋盘划分为4个2k-1×2k-1的子棋盘。这样划分后,由于原棋盘只有一个特殊方格,所以,这4个子棋盘中只有一个子棋盘包含该特殊方格,其余3个子棋盘中没有特殊方格。为了将这3个没有特殊方格的子棋盘转化为特殊棋盘,以便采用递归方法求解,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如图(b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种划分策略,直至将棋盘分割为1×1的子棋盘。
这一大堆文字是不是看着就很难受,那就别看了,
假设有一个8*8的棋盘,初始位置(2,7)处有一个棋子,那么如何填充呢,简单点说对于一共就三步
①:把nn的棋盘分成四个n/2 * n/2的小棋盘(例子中也就是分成四个44的棋盘)
②:把没有棋子的棋盘块上放上棋子,放到四个棋盘块接壤的角落(本例中也就是(4,4)(5,4)(5,5))
③:再把分出的小棋盘块再次调用上面两步,如果子棋盘长宽=2,那么直接填满
听懂了那我就直接上代码
import java.util.Scanner;
public class 棋盘覆盖 {
public static int i=1;
public static int QP[][];
public static void main(String[] args) {
Scanner scanner=new Scanner (System.in);
int size = scanner.nextInt ();
QP=new int[size][size];
int ZuoBiaoX = scanner.nextInt ()-1;
int ZuoBiaoY = scanner.nextInt ()-1;
棋盘覆盖 (0,0,ZuoBiaoX,ZuoBiaoY,size);
Print (QP);
}
public static void 棋盘覆盖(int tx,int ty ,int qx ,int qy,int size){
int numqxqy=QP[qx][qy];
if (size==2){
QP[tx][ty]=i;
QP[tx+1][ty]=i;
QP[tx][ty+1]=i;
QP[tx+1][ty+1]=i;
QP[qx][qy]=numqxqy;
i++;
return;
}
else {
int sizeA=size/2;
int qx1 = tx+sizeA-1, qx2 = tx+sizeA, qx3 = tx+sizeA-1, qx4 = tx+sizeA, qy1 = ty +sizeA-1, qy2 = ty +sizeA-1, qy3 = ty +sizeA, qy4 = ty +sizeA;
if (qx<=tx+sizeA-1){
if (qy<=ty+size-1){
qx1=qx;
qy1=qy;
QP[qx2][qy2]=i;
QP[qx3][qy3]=i;
QP[qx4][qy4]=i;
i++;
}
else {
qx3=qx;
qy3=qy;
QP[qx2][qy2]=i;
QP[qx1][qy1]=i;
QP[qx4][qy4]=i;
i++;
}
}
else {
if (qy<=ty+size-1){
qx2=qx;
qy2=qy;
QP[qx1][qy1]=i;
QP[qx3][qy3]=i;
QP[qx4][qy4]=i;
i++;
}
else {
qx4=qx;
qy4=qy;
QP[qx2][qy2]=i;
QP[qx3][qy3]=i;
QP[qx1][qy1]=i;
i++;
}
}
棋盘覆盖 (tx,ty,qx1,qy1,sizeA);
棋盘覆盖 (tx+sizeA,ty,qx2,qy2,sizeA);
棋盘覆盖 (tx,ty+sizeA,qx3,qy3,sizeA);
棋盘覆盖 (tx+sizeA,ty+sizeA,qx4,qy4,sizeA);
}
}
public static void Print(int Num[][]){
for (int i =0;i<Num.length;i++){
for (int j =0;j<Num[0].length;j++){
System.out.print (Num[i][j]+"\t");
}
System.out.println ();
}
}
}
接下来我们解释一下这个代码
首先创建一个全局变量QP,这个二维数组代表着我们的棋盘,二维数组上相同的数字就是我们的L形骨牌,i代表着我们填入期盼的棋子,一般三个一填,因为三个棋子构成一个L型骨牌。
public static int i=1;
public static int QP[][];
获取输入,size就是棋盘大小,ZuoBiaoX就是初始棋子的横坐标,ZuoBiaoY就是初始棋子的纵坐标。为什么要-1呢,在数组中是从0开始,实际我们习惯的计数是从1开始。
Scanner scanner=new Scanner (System.in);
int size = scanner.nextInt ();
QP=new int[size][size];
int ZuoBiaoX = scanner.nextInt ()-1;
int ZuoBiaoY = scanner.nextInt ()-1;
之后调用我们的棋盘覆盖方法,这个方法遵循了刚才说的三步
如果size==2,那就直接给棋盘剩下的三个格子填满i,然后return。
if (size==2){
QP[tx][ty]=i;
QP[tx+1][ty]=i;
QP[tx][ty+1]=i;
QP[tx+1][ty+1]=i;
QP[qx][qy]=numqxqy;
i++;
return;
}
否则我们把棋盘分成四块,先把中心接壤部分填上L型骨牌
int sizeA=size/2;
int qx1 = tx+sizeA-1, qx2 = tx+sizeA, qx3 = tx+sizeA-1, qx4 = tx+sizeA,
qy1 = ty +sizeA-1, qy2 = ty +sizeA-1, qy3 = ty +sizeA, qy4 = ty +sizeA;
if (qx<=tx+sizeA-1){
if (qy<=ty+size-1){
qx1=qx;
qy1=qy;
QP[qx2][qy2]=i;
QP[qx3][qy3]=i;
QP[qx4][qy4]=i;
i++;
}
else {
qx3=qx;
qy3=qy;
QP[qx2][qy2]=i;
QP[qx1][qy1]=i;
QP[qx4][qy4]=i;
i++;
}
}
else {
if (qy<=ty+size-1){
qx2=qx;
qy2=qy;
QP[qx1][qy1]=i;
QP[qx3][qy3]=i;
QP[qx4][qy4]=i;
i++;
}
else {
qx4=qx;
qy4=qy;
QP[qx2][qy2]=i;
QP[qx3][qy3]=i;
QP[qx1][qy1]=i;
i++;
}
然后分别对四个子棋盘递归,也就是16 * 16变四个 8 * 8,然后变16个4 * 4,然后变32个2 * 2,然后这32个2 * 2分别填满。
棋盘覆盖 (tx,ty,qx1,qy1,sizeA);
棋盘覆盖 (tx+sizeA,ty,qx2,qy2,sizeA);
棋盘覆盖 (tx,ty+sizeA,qx3,qy3,sizeA);
棋盘覆盖 (tx+sizeA,ty+sizeA,qx4,qy4,sizeA);
最后调用函数输出
public static void Print(int Num[][]){
for (int i =0;i<Num.length;i++){
for (int j =0;j<Num[0].length;j++){
System.out.print (Num[i][j]+"\t");
}
System.out.println ();
}
}
我们来看一下输出,基本上没什么问题,如果看了我的帖子还是没看明白,搞个4*4的棋盘按照上面的几步走一遍就懂了。
public static void Print(int Num[][]){
for (int i =0;i<Num.length;i++){
for (int j =0;j<Num[0].length;j++){
System.out.print (Num[i][j]+"\t");
}
System.out.println ();
}
}
我们来看一下输出,基本上没什么问题,如果看了我的帖子还是没看明白,搞个4*4的棋盘按照上面的几步走一遍就懂了。