Description
給定一個整數陣列
matchsticks,每個元素代表一根火柴的長度。判斷是否能用所有火柴拼成一個正方形(每根火柴恰好用一次,不能折斷)。
Example:
Input: matchsticks = [1,1,2,2,2] Output: true
Intuition#
核心思路:將火柴分成 4 組,每組的總和等於周長 / 4,用回溯法嘗試分配。
- 先算總和是否能被 4 整除,否則直接回傳 false
- 每邊目標長度 = 總和 / 4
- 把火柴分配到 4 條邊,回溯嘗試
- 關鍵優化:降序排序(大的先放),加速剪枝
Approaches#
⭐1: 回溯 + 剪枝#
- 概念: 嘗試將每根火柴放入 4 條邊中的一條,若某邊超過目標則回溯。降序排序大幅減少搜索
- 時間複雜度:
O(4^n)最差情況,但剪枝後遠小於此 - 空間複雜度:
O(n)- 遞迴深度
class Solution {
fun makesquare(matchsticks: IntArray): Boolean {
val total = matchsticks.sum()
if (total % 4 != 0) return false
val side = total / 4
// 降序排序,讓大的先嘗試,提早剪枝
matchsticks.sortDescending()
// 若最大的火柴超過邊長,不可能
if (matchsticks[0] > side) return false
val sides = IntArray(4)
fun backtrack(idx: Int): Boolean {
if (idx == matchsticks.size) {
return sides.all { it == side }
}
val seen = mutableSetOf<Int>()
for (i in 0 until 4) {
// 剪枝:放入後超過邊長
if (sides[i] + matchsticks[idx] > side) continue
// 剪枝:相同長度的邊不重複嘗試
if (sides[i] in seen) continue
seen.add(sides[i])
sides[i] += matchsticks[idx]
if (backtrack(idx + 1)) return true
sides[i] -= matchsticks[idx]
}
return false
}
return backtrack(0)
}
}🔑 Takeaways#
- Pattern: K 等分問題 → 回溯分配到 K 個桶
- 關鍵技巧: 降序排序是關鍵優化(大元素先放可以提早發現不合法的分支);跳過相同邊長度的桶避免重複搜索。此 pattern 同樣適用於 698. Partition to K Equal Sum Subsets