棋盘覆盖问题分析
前言:如果你想通过这篇文章了解 解题思路 那么你来对地方了,当然 借鉴 分析代码也是可以的,文章最后我会附上完整的代码。(如果对你有帮助请点个赞,原创不容易,码字太累了)
(1)问题描述:在一个2^k * 2k(k为正整数,k<=10,length=2k)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格(其坐标为aa,bb,分别代表行坐标号和列坐标号),以及有四种L型骨牌(如下图)。求用若干块这种L型骨牌实现除该特殊点棋盘的全覆盖。
输入格式: 输入三个数,分别是aa,bb,length.
输出格式: 输出整个棋盘。其中特殊方格填为0,然后铺棋盘的顺序为:先铺四个子棋盘交界的部分,然后递归的对每个子棋盘按照左上,右上,右下,左下的顺时针顺序铺满棋盘。每一块骨牌中三个方格数字相同,按照顺序标号,即第一块骨牌全标为1,第二块骨牌全标为2,…,以此类推。输出的每个数占4个场宽,右对齐。
输入样例:
1 1 4
表示:特殊格子为(1,1),棋盘有4行4列
输出样例:
0 2 3 3
2 2 1 3
5 1 1 4
5 5 4 4
表示:先铺三个1(一块L型骨牌),再铺三个2,…,最后铺三个5。
解题思路:
这道题可以用 递归 和 分而治之 的方法来做,先来看看要递归啥。
(1) 递归:以该图可以观察到,这是一个 8 X 8 的方格阵,根据题目要求方格阵大小是 2^k * 2^k(k为正整数,k<=10,length=2k),我们可以把大的方格阵从中心边界不断的等分为4份,直到方格阵长度为1。所以我们要递归的目的就是在一个更小的方格阵内根据特殊点的位置判断应该用哪一种L骨牌去处理。
(2) 分而治之:先看一个 4 X 4 的方格纸,发现特殊点只能是四个区域的一个,根据特殊点在中心边界的右上,右下,左下,左上方分别用4种 对应的L骨牌 去处理。
回到之前的图,我们确定中心边界后发现特殊点在左上方所以用最后一种L骨牌去处理。
当处理完之后,把这 L骨牌占的3个点也当作特殊点 和之前的特殊点一起按照顺序处理左上,右上,右下,左下的小方格阵的方法调用递归函数。
(3)以左上方的区域为例,一步一步的分析下看是否符合我们的想法。
在 4 X 4 方格阵的中心边界处发现特殊点在右下角,所以用第二种骨牌处理,按照上面的方法然后再调用递归函数。
(4)在4个 2 X 2 方格阵里判断特殊点的位置再用骨牌处理,分别得到下图4个 2 X 2 方格阵。
(5)所以对于 8 X 8 方格来说左上角处理完后的 4 X 4 方格阵是这样的。
代码分析:
(1)先根据输入的长度length和特殊点的坐标创建一个二维的容器,这里之所以是 <=length 是不用第0行和第0列;
for(int i=0;i<=length;i++){
vector<int> newVecInt;
vec.push_back(newVecInt);
for(int j=0;j<=length;j++){
vec[i].push_back(0);
}
}
vec[aa][bb]=0;
(2)4个if语句来判断特殊点在中心边界的那个区域,
新增的两个变量 curRow 和 curCol 分别是当前这个方格阵的起始坐标,例如对于4个 4 X 4 方格阵来说他们的当前行分别如图示,作用是根据点的位置来确定L骨牌应该放的位置
以 8 X 8 为例,特殊点(4,4),当前行列是1,1(因为第一次调用函数时的参数是handle(aa,bb,length,1,1))
以中心点为基准来判断可以算出坐标:(curRow + length/2 - 1,curCol + length/2 - 1)=(4,4)
此时特殊点在中心点上说明是在左上方(这里看代码注释)
void handle(int aa,int bb,int length,int curRow,int curCol){
if(length==1)//最小的棋盘
return;
if(aa<=curRow+length/2-1 && bb<=curCol+length/2-1){//点在左上角
}
if(aa<=curRow+length/2-1 && bb>curCol+length/2-1){//点在右上角
}
if(aa>curRow+length/2-1 && bb<=curCol+length/2-1){//点在左下角
}
if(aa>curRow+length/2-1 && bb>curCol+length/2-1){//点在右下角
}
}
(3)我们在这个if语句里我们加入对应的L骨牌(这里cnt初始化为1)为了加入L方块需要更改当前curRow和curCol,此时(curRow,curCol)=(2,2),对(2,2)、(3,2)、(2,3)的完成赋值,再将次数加1 (这里tempRow和tempCol用来保存未变更之前的行列,之后有用)
int tempRow=curRow;
int tempCol=curCol;
curRow=length/2+curRow-1;
curCol=length/2+curCol-1;
vec[curRow][curCol] = cnt;
vec[curRow+1][curCol]=cnt;
vec[curRow][curCol+1]=cnt;
cnt++;
(4)弄好L骨牌后,就要按照顺序依次弄左上,右上,右下,左下的小方格阵,为了确定每个方格阵的起始行列就要用到之前的tempRow/Col,稍微做一点运算就确定好了。前两个参数我们把之前赋值的L骨牌位置当作特殊点传递,这里注意第三行代码他的前两个参数是aa,bb,这里我们看上图的蓝色点位置,就明白了。(这4行代码可以根据上图结合第三步给出的参数值分析)
handle(curRow,curCol,length/2,tempRow,tempCol);//左上
handle(curRow,curCol+1,length/2,tempRow,curCol+1);//右上
handle(aa,bb,length/2,curRow+1,curCol+1);//右下
handle(curRow+1,curCol,length/2,curRow+1,tempCol);//左下
完整代码:
上面给出的代码是其中一种情况的处理,4个区域因此有4种处理,对每一种稍微做点修改就好了, 下面给出完整代码:
//@copyright pclglutrjc1
#include<iostream>
#include<algorithm>
#include<vector>