算法时空-乐高积木问题

在这里插入图片描述

乐高铺砖问题

地板长度都是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));

    }


}


结果展示

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值