1240 Tiling a Rectangle with the Fewest Squares 鋪瓷磚
問題描述
你是一位施工隊的工長,根據設計師的要求准備為一套設計風格獨特的房子進行室內裝修。
房子的客廳大小為 n
x m
,為保持極簡的風格,需要使用盡可能少的 正方形 瓷磚來鋪蓋地面。
假設正方形瓷磚的規格不限,邊長都是整數。
請你幫設計師計算一下,最少需要用到多少塊方形瓷磚?
示例 1:
輸入: n = 2, m = 3
輸出: 3
**解釋:**3
塊地磚就可以鋪滿卧室。
2
塊1x1 地磚
1
塊2x2 地磚
示例 2:
輸入: n = 5, m = 8
輸出: 5
示例 3:
輸入: n = 11, m = 13
輸出: 6
提示:
1 <= n <= 13
1 <= m <= 13
思路
- 讀題
給定固定面積的房間N*M, 使用正方形
瓷磚剛好填滿, 瓷磚長度不定, 要求使用瓷磚數量最少
貪心思想+動規實現+特例排除
- 貪心
每次選取最大長度的正方形分割(當前最優解), 以此將區塊分成兩份, 其中一份繼續貪心的選取最大長度的正方形分割 - 動規
每個區塊可以分成兩個小區塊, 小區塊又可以繼續划分 --> 小區塊的最優解合並成大區塊的最優解 - 特例
11*13
這個區塊 最優解不能完全分割成兩個不相關的區塊
- 一般區塊的最優解分3種情況:
- N=1 或者 M=1 即只能平鋪
1*1
的方塊 M或N個 - N=M 直接一步到位 鋪上
N*N
的方塊 1個 - 其他情況 將區塊橫着切分或者豎着切分 找到最小的那個方案
- N=1 或者 M=1 即只能平鋪
打表法
因為運算的數據量不大 13*13
可以手動生成全局答案
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8]
[3, 3, 1, 4, 4, 2, 5, 5, 3, 6, 6, 4, 7]
[4, 2, 4, 1, 5, 3, 5, 2, 6, 4, 6, 3, 7]
[5, 4, 4, 5, 1, 5, 5, 5, 6, 2, 6, 6, 6]
[6, 3, 2, 3, 5, 1, 5, 4, 3, 4, 6, 2, 6]
[7, 5, 5, 5, 5, 5, 1, 7, 6, 6, 6, 6, 6]
[8, 4, 5, 2, 5, 4, 7, 1, 7, 5, 6, 3, 6]
[9, 6, 3, 6, 6, 3, 6, 7, 1, 6, 7, 4, 7]
[10, 5, 6, 4, 2, 4, 6, 5, 6, 1, 6, 5, 7]
[11, 7, 6, 6, 6, 6, 6, 6, 7, 6, 1, 7, 6]
[12, 6, 4, 3, 6, 2, 6, 3, 4, 5, 7, 1, 7]
[13, 8, 7, 7, 6, 6, 6, 6, 7, 7, 6, 7, 1]
代碼實現
動態規划
public class Solution {
private static int size = 13;
private static int[][] dp = new int[size + 1][size + 1];
static {
// 初始化為-1 表示沒有被修改
for (int i = 1; i <= size; i++) {
Arrays.fill(dp[i], -1);
}
for (int i = 1; i <= size; i++) {
// i*1 or 1*i 面積的鋪瓷磚數量
dp[i][1] = i;
dp[1][i] = i;
// i*i 面積的鋪瓷磚數量
dp[i][i] = 1;
}
// 唯一的特殊情況 11*13 or 13*11 面積的鋪瓷磚數量 不符合貪心規則
int specialCaseW = 11, specialCaseH = 13, specialCaseR = 6;
dp[specialCaseH][specialCaseW] = specialCaseR;
dp[specialCaseW][specialCaseH] = specialCaseR;
}
public int tilingRectangle(int n, int m) {
return helper(n, m);
}
private int helper(int n, int m) {
if (dp[n][m] != -1) {
return dp[n][m];
}
// 最大分塊 n*m個1*1方塊
int res = n * m, tmp;
// 橫着切
for (int i = 1; i < n; i++) {
tmp = helper(i, m) + helper(n - i, m);
res = Math.min(res, tmp);
}
// 豎着切
for (int i = 1; i < m; i++) {
tmp = helper(n, i) + helper(n, m - i);
res = Math.min(res, tmp);
}
// 備忘錄 n*m == m*n
dp[n][m] = dp[m][n] = res;
return res;
}
}