title: Matchsticks to Square slug: matchsticks-to-square difficulty: medium leetcode_id: 473 leetcode_url: https://leetcode.com/problems/matchsticks-to-square/ categories: - arrays - recursion patterns: - slug: backtracking is_optimal: true function_signature: "def makesquare(matchsticks: list[int]) -> bool:" test_cases: visible: - input: { matchsticks: [1, 1, 2, 2, 2] } expected: true - input: { matchsticks: [3, 3, 3, 3, 4] } expected: false hidden: - input: { matchsticks: [1, 1, 1, 1] } expected: true - input: { matchsticks: [1, 2, 3] } expected: false - input: { matchsticks: [5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3] } expected: true - input: { matchsticks: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] } expected: false - input: { matchsticks: [2, 2, 2, 2, 2, 2, 2, 2] } expected: true - input: { matchsticks: [10] } expected: false - input: { matchsticks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] } expected: true description: | You are given an integer array `matchsticks` where `matchsticks[i]` is the length of the ith matchstick. You want to use **all the matchsticks** to make one square. You **should not break** any stick, but you can link them up, and each matchstick must be used **exactly one time**. Return `true` if you can make this square and `false` otherwise. constraints: | - `1 <= matchsticks.length <= 15` - `1 <= matchsticks[i] <= 10^8` examples: - input: "matchsticks = [1,1,2,2,2]" output: "true" explanation: "You can form a square with length 2. One side of the square comes from two sticks with length 1." - input: "matchsticks = [3,3,3,3,4]" output: "false" explanation: "You cannot find a way to form a square with all the matchsticks." explanation: intuition: | Imagine you have a pile of matchsticks on a table, and you need to arrange them into four groups — one for each side of a square. Every matchstick must be used exactly once, and all four sides must have the **same total length**. The first insight is mathematical: for a square to be possible, the total length of all matchsticks must be **divisible by 4**. If it isn't, there's no way to form four equal sides, so we can immediately return `false`. The second insight is about problem structure: this is a **partition problem**. We need to partition the array into 4 subsets where each subset sums to `total_length / 4`. This is a classic backtracking scenario — we try placing each matchstick into one of the four sides, and if we hit a dead end, we backtrack and try a different placement. Think of it like this: you pick up each matchstick and ask, "Which side should this go on?" You try the first side, and if you can eventually complete the square, great! If not, you take the matchstick back and try another side. This systematic exploration guarantees we find a solution if one exists. approach: | We solve this using **Backtracking with Pruning**: **Step 1: Check if a square is possible** - Calculate the total sum of all matchsticks - If `total % 4 != 0`, return `false` immediately — a square is impossible - Calculate `side_length = total // 4` — this is what each side must sum to   **Step 2: Sort matchsticks in descending order (optimisation)** - Sorting largest-first helps us fail faster - Large matchsticks are harder to place, so if they can't fit, we discover this early - This dramatically reduces the search space in practice   **Step 3: Use backtracking to fill four sides** - Create an array `sides = [0, 0, 0, 0]` to track the current length of each side - For each matchstick, try adding it to each of the four sides - If adding would exceed `side_length`, skip that side (pruning) - If we successfully place all matchsticks, return `true` - If no valid placement exists, backtrack by removing the matchstick and trying the next side   **Step 4: Additional pruning** - Skip duplicate side values: if `sides[i] == sides[i-1]` and we already failed with `sides[i-1]`, skip `sides[i]` - Early termination: if the first matchstick is larger than `side_length`, return `false`   **Step 5: Return the result** - If the backtracking completes successfully (all matchsticks placed), return `true` - If we exhaust all possibilities, return `false` common_pitfalls: - title: Forgetting the Divisibility Check description: | If the total sum of matchsticks isn't divisible by 4, no valid square exists. Always check this first. For example, with `matchsticks = [1, 2, 3]`, the sum is `6`. Since `6 % 4 = 2 != 0`, we can immediately return `false` without any backtracking. This simple check can save enormous computation time. wrong_approach: "Start backtracking without checking divisibility" correct_approach: "Return false early if total % 4 != 0" - title: Not Sorting for Efficiency description: | Without sorting, you might try many small matchsticks first, build up partial sides, then discover a large matchstick doesn't fit anywhere. All that work is wasted. By sorting in **descending order**, you try placing the largest (most constrained) matchsticks first. If they can't fit, you fail fast and prune massive branches of the search tree. With `n = 15` matchsticks and `4^15` potential placements, pruning is essential. wrong_approach: "Process matchsticks in original order" correct_approach: "Sort descending to fail fast on large matchsticks" - title: Treating This as a Simple DP Problem description: | While dynamic programming with bitmasks can solve this (tracking which matchsticks are used), the state space is `2^n * 4` which is manageable for `n <= 15`. However, backtracking with good pruning is often simpler to implement and understand. Both approaches work, but backtracking with descending sort is typically faster in practice due to aggressive early termination. wrong_approach: "Overcomplicating with bitmask DP when backtracking suffices" correct_approach: "Use backtracking with sorting and pruning" - title: Missing Duplicate Side Pruning description: | If two sides have the same current length and we tried (and failed) adding a matchstick to one, there's no point trying the other — the result will be identical. For example, if `sides = [3, 3, 0, 0]` and adding the current matchstick to `sides[0]` leads to failure, adding it to `sides[1]` will fail the same way. Skipping these duplicate attempts significantly reduces redundant work. wrong_approach: "Try every side even when some have identical values" correct_approach: "Skip sides with the same value as a previously failed attempt" key_takeaways: - "**Partition problems and backtracking**: When you need to divide elements into groups with constraints, backtracking systematically explores all possibilities" - "**Pruning is essential**: Without optimisations like sorting and duplicate skipping, backtracking can be impractically slow" - "**Early termination**: Simple checks (divisibility, single element too large) can eliminate entire problem instances instantly" - "**Related problems**: This pattern appears in Partition Equal Subset Sum, Partition to K Equal Sum Subsets, and Fair Distribution of Cookies" time_complexity: "O(4^n) in the worst case, where `n` is the number of matchsticks. Each matchstick can potentially go into any of the 4 sides. However, pruning (sorting, duplicate skipping, sum checks) dramatically reduces this in practice." space_complexity: "O(n) for the recursion call stack depth, plus O(1) for the sides array." solutions: - approach_name: Backtracking with Pruning is_optimal: true code: | def makesquare(matchsticks: list[int]) -> bool: total = sum(matchsticks) # A square needs 4 equal sides if total % 4 != 0: return False side_length = total // 4 # Sort descending to try large matchsticks first (fail fast) matchsticks.sort(reverse=True) # Early check: if largest matchstick > side_length, impossible if matchsticks[0] > side_length: return False # Track current length of each of the 4 sides sides = [0, 0, 0, 0] def backtrack(index: int) -> bool: # Base case: all matchsticks placed successfully if index == len(matchsticks): return True stick = matchsticks[index] # Try placing this matchstick on each side for i in range(4): # Pruning: skip if this side would exceed target if sides[i] + stick > side_length: continue # Pruning: skip duplicate sides (same current length) if i > 0 and sides[i] == sides[i - 1]: continue # Place the matchstick sides[i] += stick # Recurse to place the next matchstick if backtrack(index + 1): return True # Backtrack: remove the matchstick sides[i] -= stick # No valid placement found for this matchstick return False return backtrack(0) explanation: | **Time Complexity:** O(4^n) worst case — each matchstick can go into 4 sides. Pruning reduces this significantly in practice. **Space Complexity:** O(n) — recursion depth equals the number of matchsticks. We try placing each matchstick into one of four sides. Sorting largest-first means we quickly discover when a large matchstick can't fit anywhere. Skipping duplicate side values avoids redundant exploration. If we place all matchsticks without any side exceeding the target length, we've found a valid square. - approach_name: Brute Force (No Pruning) is_optimal: false code: | def makesquare(matchsticks: list[int]) -> bool: total = sum(matchsticks) if total % 4 != 0: return False side_length = total // 4 sides = [0, 0, 0, 0] def backtrack(index: int) -> bool: if index == len(matchsticks): # Check if all sides equal return all(s == side_length for s in sides) stick = matchsticks[index] for i in range(4): if sides[i] + stick <= side_length: sides[i] += stick if backtrack(index + 1): return True sides[i] -= stick return False return backtrack(0) explanation: | **Time Complexity:** O(4^n) — without pruning, explores many redundant branches. **Space Complexity:** O(n) — recursion depth. This version works but lacks the critical optimisations. Without sorting, it may build partial solutions with small matchsticks only to fail later on large ones. Without duplicate pruning, it explores identical configurations multiple times. For the constraint `n <= 15`, this can be noticeably slower than the optimised version.