1240. 铺瓷砖
苦此题 好几个小时,详细记录一下学习大佬的解题思路,在数据范围内能通过的一个解法
思路详细记录
- 铺设瓷砖,最开始从给定范围内
(n, m)
的左上角开始铺,那么可以铺
- 最小瓷砖的边长为
1
- 最大瓷砖的边长为
min(n, m)
- 按照行铺设瓷砖,第一行铺满了,铺第二行直到所有行铺满
- 铺设策略
- 每一次铺瓷砖,可以从当前起点计算空白处允许铺设瓷砖的 最大边长
- 然后 从 边长
1
到最大边长mx
每一次有mx
种铺设方法 - 直到找到最少需要的瓷砖数量
代码实现思路
- 一个位置只有 被铺/没有被铺,并且数据范围是
[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);
}
}
}
}
- 真难啊