1240. 铺瓷砖

苦此题 好几个小时,详细记录一下学习大佬的解题思路,在数据范围内能通过的一个解法


思路详细记录

  1. 铺设瓷砖,最开始从给定范围内(n, m)的左上角开始铺,那么可以铺
  1. 最小瓷砖的边长为 1
  2. 最大瓷砖的边长为 min(n, m)
  1. 按照行铺设瓷砖,第一行铺满了,铺第二行直到所有行铺满
  2. 铺设策略
  1. 每一次铺瓷砖,可以从当前起点计算空白处允许铺设瓷砖的 最大边长
  2. 然后 从 边长 1到最大边长mx 每一次有 mx 种铺设方法
  3. 直到找到最少需要的瓷砖数量

代码实现思路

  • 一个位置只有 被铺/没有被铺,并且数据范围是[1, 13],可以用一个13位的二进制数来表示每一行瓷砖的铺设状态
  • n 为行,那么即创建长度为n的数组——>int[] filled = new int[n];
  • 在范围n*m内铺设瓷砖,所有的瓷砖都铺最小的,可以铺 n*m
private int n;
private int m;
private int ans;
private int[] filled;

public int tilingRectangle(int n, int m) {
    this.n = n;
    this.m = m;
    ans = n * m;
    filled = new int[n];

    // 从 (0, 0) 位置开始铺设,第三个参数记录所用的瓷砖数
    dfs(0, 0, 0);

    return ans;
}
  • 递归函数逻辑如何实现:签名为 dfs(int i, int j,int cnt)
  • 首先是终止条件
  • j == m的时候表示当前行被铺满了,那么这个时候i++跳到下一行继续铺
  • i == n的时候表示铺满了,退出循环
  • 递归逻辑
  • 首先判断当前位置有没有被铺设瓷砖
  • 从当前位置开始找允许铺设瓷砖的最大边长mx
  • 然后进行剪枝
  • 然后对当前方案所标记过的,即所铺过的瓷砖地方进行还原
private void dfs(int i, int j, int cnt) {
    if(j == m) {
        i++;
        j = 0;
    }
    if(i == n) {
        ans = cnt;
        return;
    }

    //  判断当前位置是否铺设了瓷砖
    if((filled[i] & (1 << j)) == 1) {
        dfs(i, j+1, cnt);
    } else if(cnt + 1 < ans) {
        // 寻找最大边长 mx
        // 从当前节点行 和 列分别展开进行寻找
        int r = 0,c = 0;
        //  向下统计
        for(int k = i;k < n;k++) {
            // 表示该位置被 填充了
            if((filled[k] >> j & 1) == 1) {
                break;
            }
            r++;
        }
        // 向右统计
        for(int k = j;k < m;k++) {
            if((filled[i] >> k & 1) == 1) {
                break;
            }
            c++;
        }
        int mx = Math.min(r, c);

        // 开始铺
        for(int w = 1;w <= mx;w++) {
            for(int k = 0;k < i;k++) {
                // 标记第  xx 行 的 第几个  
                filled[i + w - 1] |= (1 << (j + k));
                // 标记第  xx 列 的 第几个
                filled[i + k] |= (1 << (j + w - 1));
            }
            // 还是当前行,然后 j 列增加 w
            dfs(i, j + w, t + 1);
        }

        // 复位
        for(int x = i;x < i + mx;x++) {
            for(int y = j;y < j + mx;y++) {
                filled[x] ^= (1 << y);
            }
        }
    }
}
  • 真难啊

完结撒花