乐高铺砖问题
地板长度都是2的幂,铺的都是唯一的同一种砖(如上图所示),但地板上都会有一水泥点已经被覆盖,无需再用铺砖,试问能否针对此时的情况,如果存在的话,如何制定出铺砖方案,怎么铺?
- 首先是从数学上简单的证明了4^m-1一定能被3整除,但并不一定存在铺砖方案;
- 此时问题可以这样来考虑,边长是L(L是2的幂)的地板上,在某一个固定坐标上存在已经铺好水泥的无需再铺方砖的点,那么这个问题可以一步步缩减为小问题,递归来解决它;
- 首先对L的地板进行等分,分成大小相等的4块,每块长都是L/2;如果不做处理,继续对小块地板进行铺砖时候,那么就会造成两种情况,一种是没有水泥点,一种是带有老水泥点,那么情况就会变的复杂,此时再独立出来进行小块地板的铺砖时候,就会出问题(出什么问题?此刻是4^m已经不再是3的倍数了,所以必定有空白点剩下和其他小块地板进行拼接铺盖),那么如何解决这个问题?
- 只需要在4块方砖交接的中间那4个点,找出离已知水泥点最远的3个,用一块方砖覆盖上即可,这样再递归处理每个小问题时,每块小方砖都是带有唯一一个水泥点的子问题了;
代码实现
import java.util.*;
public class Main {
static int len = 8; //地板长度,得是2的幂,这里取8做个demo
static int[][] LG = new int[len][len]; //模拟地板的二维数组,初始化时的水泥点为-1,其余都是0,而后将所有0都变为砖块的序号来表示已经铺完
static int index = 1; //全局铺砖的序号,每铺一块就加1
//坐标顶点类,用于存储二维信息
private static class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) {
Random r = new Random();
int x0 = r.nextInt(len);
int y0 = r.nextInt(len);
//p0是任意选定的唯一水泥点
Point p0 = new Point(x0, y0);
Point ps = new Point(0, 0); //ps是选定初始坐标原点,即(start点)
LG[x0][y0] = -1; // 已经铺好水泥点的地方;
//打印初始地板信息
for (int i=0; i<LG.length; i++)
System.out.println(Arrays.toString(LG[i]));
System.out.println();
lgImplement(len, p0, ps); //递归铺砖
//打印铺完后的地板信息
for (int i=0; i<LG.length; i++)
System.out.println(Arrays.toString(LG[i]));
}
private static void lgImplement(int l, Point p0, Point ps) {
if (l <= 1) return; //默认每次调用这个函数时,都有一个水泥点,所以当只有1个格子时候,必定不用再铺砖了
// 下面求出4个中间接点坐标;
Point[][] p = new Point[2][2];
p[0][1] = new Point(ps.x + l / 2 - 1, ps.y + l / 2); //第一象限点
p[0][0] = new Point(p[0][1].x, p[0][1].y - 1); //第二象限点
p[1][0] = new Point(p[0][1].x + 1, p[0][1].y - 1); // 第三象限点
p[1][1] = new Point(p[0][1].x + 1, p[0][1].y); // 第四象限点
// 求算水泥点是在第几象限;(默认二维数组的行对应x轴,列对应y轴)
int i = (p0.x - ps.x) * 2 / l;
int j = (p0.y - ps.y) * 2 / l;
int temp = LG[p[i][j].x][p[i][j].y]; //先取出原本该水泥点上的值(可能是0,也可能是-1)
// 全部赋值为砖序号数,避免了做判断
LG[p[0][0].x][p[0][0].y] = index;
LG[p[0][1].x][p[0][1].y] = index;
LG[p[1][0].x][p[1][0].y] = index;
LG[p[1][1].x][p[1][1].y] = index ++; //方砖序号自增
LG[p[i][j].x][p[i][j].y] = temp; // 然后将原来值取回,不能改变原水泥点信息;
p[i][j] = p0; //并且为了避免接下来的递归做判断,将原水泥点赋值给p二维数组;
// 递归进行铺砖;
lgImplement(l/2, p[0][1], new Point(ps.x, ps.y + l/2));
lgImplement(l/2, p[0][0], ps);
lgImplement(l/2, p[1][0], new Point(ps.x + l/2, ps.y));
lgImplement(l/2, p[1][1], new Point(ps.x + l/2, ps.y + l/2));
}
}