Missing undo step on backtrack
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Given a rectangle of size n x m, return the minimum number of integer-sided squares that tile the rectangle.
Example 1:
Input: n = 2, m = 3 Output: 3 Explanation:3squares are necessary to cover the rectangle.2(squares of1x1)1(square of2x2)
Example 2:
Input: n = 5, m = 8 Output: 5
Example 3:
Input: n = 11, m = 13 Output: 6
Constraints:
1 <= n, m <= 13Problem summary: Given a rectangle of size n x m, return the minimum number of integer-sided squares that tile the rectangle.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Backtracking
2 3
5 8
11 13
selling-pieces-of-wood)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1240: Tiling a Rectangle with the Fewest Squares
class Solution {
private int n;
private int m;
private int[] filled;
private int ans;
public int tilingRectangle(int n, int m) {
this.n = n;
this.m = m;
ans = n * m;
filled = new int[n];
dfs(0, 0, 0);
return ans;
}
private void dfs(int i, int j, int t) {
if (j == m) {
++i;
j = 0;
}
if (i == n) {
ans = t;
return;
}
if ((filled[i] >> j & 1) == 1) {
dfs(i, j + 1, t);
} else if (t + 1 < ans) {
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 < w; ++k) {
filled[i + w - 1] |= 1 << (j + k);
filled[i + k] |= 1 << (j + w - 1);
}
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;
}
}
}
}
}
// Accepted solution for LeetCode #1240: Tiling a Rectangle with the Fewest Squares
func tilingRectangle(n int, m int) int {
ans := n * m
filled := make([]int, n)
var dfs func(i, j, t int)
dfs = func(i, j, t int) {
if j == m {
i++
j = 0
}
if i == n {
ans = t
return
}
if filled[i]>>j&1 == 1 {
dfs(i, j+1, t)
} else if t+1 < ans {
var r, c int
for k := i; k < n; k++ {
if filled[k]>>j&1 == 1 {
break
}
r++
}
for k := j; k < m; k++ {
if filled[i]>>k&1 == 1 {
break
}
c++
}
mx := min(r, c)
for w := 1; w <= mx; w++ {
for k := 0; k < w; k++ {
filled[i+w-1] |= 1 << (j + k)
filled[i+k] |= 1 << (j + w - 1)
}
dfs(i, j+w, t+1)
}
for x := i; x < i+mx; x++ {
for y := j; y < j+mx; y++ {
filled[x] ^= 1 << y
}
}
}
}
dfs(0, 0, 0)
return ans
}
# Accepted solution for LeetCode #1240: Tiling a Rectangle with the Fewest Squares
class Solution:
def tilingRectangle(self, n: int, m: int) -> int:
def dfs(i: int, j: int, t: int):
nonlocal ans
if j == m:
i += 1
j = 0
if i == n:
ans = t
return
if filled[i] >> j & 1:
dfs(i, j + 1, t)
elif t + 1 < ans:
r = c = 0
for k in range(i, n):
if filled[k] >> j & 1:
break
r += 1
for k in range(j, m):
if filled[i] >> k & 1:
break
c += 1
mx = r if r < c else c
for w in range(1, mx + 1):
for k in range(w):
filled[i + w - 1] |= 1 << (j + k)
filled[i + k] |= 1 << (j + w - 1)
dfs(i, j + w, t + 1)
for x in range(i, i + mx):
for y in range(j, j + mx):
filled[x] ^= 1 << y
ans = n * m
filled = [0] * n
dfs(0, 0, 0)
return ans
// Accepted solution for LeetCode #1240: Tiling a Rectangle with the Fewest Squares
struct Solution;
use std::collections::HashMap;
impl Solution {
fn tiling_rectangle(n: i32, m: i32) -> i32 {
let mut memo: HashMap<(i32, i32), i32> = HashMap::new();
Self::dp(n, m, &mut memo)
}
fn dp(n: i32, m: i32, memo: &mut HashMap<(i32, i32), i32>) -> i32 {
if n == m {
1
} else {
if let Some(&res) = memo.get(&(n, m)) {
return res;
}
let mut res = std::i32::MAX;
for i in 1..n {
res = res.min(Self::dp(i, m, memo) + Self::dp(n - i, m, memo));
}
for j in 1..m {
res = res.min(Self::dp(n, j, memo) + Self::dp(n, m - j, memo));
}
for i in 1..n - 1 {
for j in 1..m - 1 {
let a = Self::dp(i, j + 1, memo);
let b = Self::dp(i + 1, m - j - 1, memo);
let c = Self::dp(n - i, j, memo);
let d = Self::dp(n - i - 1, m - j, memo);
res = res.min(a + b + c + d + 1);
}
}
memo.insert((n, m), res);
res
}
}
}
#[test]
fn test() {
let n = 2;
let m = 3;
let res = 3;
assert_eq!(Solution::tiling_rectangle(n, m), res);
let n = 5;
let m = 8;
let res = 5;
assert_eq!(Solution::tiling_rectangle(n, m), res);
let n = 11;
let m = 13;
let res = 6;
assert_eq!(Solution::tiling_rectangle(n, m), res);
let n = 7;
let m = 6;
let res = 5;
assert_eq!(Solution::tiling_rectangle(n, m), res);
let n = 10;
let m = 9;
let res = 6;
assert_eq!(Solution::tiling_rectangle(n, m), res);
}
// Accepted solution for LeetCode #1240: Tiling a Rectangle with the Fewest Squares
function tilingRectangle(n: number, m: number): number {
let ans = n * m;
const filled: number[] = new Array(n).fill(0);
const dfs = (i: number, j: number, t: number) => {
if (j === m) {
++i;
j = 0;
}
if (i === n) {
ans = t;
return;
}
if ((filled[i] >> j) & 1) {
dfs(i, j + 1, t);
} else if (t + 1 < ans) {
let [r, c] = [0, 0];
for (let k = i; k < n; ++k) {
if ((filled[k] >> j) & 1) {
break;
}
++r;
}
for (let k = j; k < m; ++k) {
if ((filled[i] >> k) & 1) {
break;
}
++c;
}
const mx = Math.min(r, c);
for (let w = 1; w <= mx; ++w) {
for (let k = 0; k < w; ++k) {
filled[i + w - 1] |= 1 << (j + k);
filled[i + k] |= 1 << (j + w - 1);
}
dfs(i, j + w, t + 1);
}
for (let x = i; x < i + mx; ++x) {
for (let y = j; y < j + mx; ++y) {
filled[x] ^= 1 << y;
}
}
}
};
dfs(0, 0, 0);
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.