LeetCode 1284 - Minimum Number of Flips to Convert Binary Matrix to Zero Matrix
The problem asks us to transform a given binary matrix mat into a zero matrix, where all elements are 0. Each operation
Difficulty: 🔴 Hard
Topics: Array, Hash Table, Bit Manipulation, Breadth-First Search, Matrix
Solution
Problem Understanding
The problem asks us to transform a given binary matrix mat into a zero matrix, where all elements are 0. Each operation allows you to flip a cell and its four immediate neighbors (up, down, left, right). Flipping means changing a 1 to 0 or a 0 to 1. The input is an m x n binary matrix with dimensions constrained between 1 and 3 for both m and n, which means the matrix is always very small. The output is the minimum number of flips needed to convert the matrix into a zero matrix. If it's impossible, we return -1.
The constraints, specifically 1 <= m, n <= 3, hint that a brute-force exploration of all possible flip sequences is feasible. However, a naive implementation that tries all possible sequences without optimization would still require careful design to avoid redundant work. Important edge cases include matrices that are already zeros (requiring zero flips), matrices that cannot be converted (requiring -1), and matrices with only one row or column.
Approaches
The brute-force approach involves trying every combination of flips on the matrix. For a matrix of size m x n, there are 2^(m*n) possible ways to flip cells, because each cell can either be flipped or not. For each configuration, you apply the flips and check if the resulting matrix is zero. While this guarantees correctness, the exponential time complexity makes it impractical for larger matrices. Even though the maximum m*n is 9, 2^9 = 512 is manageable but still not elegant.
The key insight for an optimal approach is that the small size of the matrix allows us to use Breadth-First Search (BFS) combined with bitmasking. We can represent the matrix as a single integer, where each bit represents a cell. BFS explores all possible flip states level by level, guaranteeing the minimum number of steps to reach the zero matrix. We also use a set to keep track of visited states to avoid revisiting the same configuration. Bit manipulation makes flipping neighbors efficient.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(2^(m_n) * m_n) | O(1) | Try all possible flip combinations and check result |
| Optimal (BFS + Bitmask) | O(2^(m_n) * m_n) | O(2^(m*n)) | BFS on state space using bitmasking to represent matrices, track visited states |
Algorithm Walkthrough
-
Convert the matrix into an integer
start, where each bit represents a cell. Bit1means the cell is1and bit0means the cell is0. This allows fast comparison and flipping. -
Define a helper function
flip(mask, r, c)that flips the cell at(r, c)and its neighbors using bitwise XOR operations. -
Initialize a BFS queue starting with
(start, 0)where0is the number of steps taken. -
Use a set
seento track visited masks to avoid repeated work. -
While the queue is not empty:
-
Pop a mask and its corresponding step count.
-
If the mask is zero, return the step count.
-
Otherwise, for each cell
(r, c)in the matrix, compute the new mask after flipping it and enqueue it if not already seen. -
If BFS ends without finding a zero mask, return
-1.
Why it works: BFS explores all states in increasing number of flips. The first time we encounter a zero mask corresponds to the minimum number of flips required. Bitmasking ensures constant-time flipping and comparisons. Tracking visited states ensures we do not loop indefinitely.
Python Solution
from collections import deque
from typing import List
class Solution:
def minFlips(self, mat: List[List[int]]) -> int:
m, n = len(mat), len(mat[0])
start = 0
for i in range(m):
for j in range(n):
if mat[i][j]:
start |= 1 << (i * n + j)
def flip(mask: int, r: int, c: int) -> int:
for dr, dc in [(0,0),(0,1),(0,-1),(1,0),(-1,0)]:
nr, nc = r + dr, c + dc
if 0 <= nr < m and 0 <= nc < n:
mask ^= 1 << (nr * n + nc)
return mask
queue = deque([(start, 0)])
seen = {start}
while queue:
mask, steps = queue.popleft()
if mask == 0:
return steps
for i in range(m):
for j in range(n):
new_mask = flip(mask, i, j)
if new_mask not in seen:
seen.add(new_mask)
queue.append((new_mask, steps + 1))
return -1
The implementation first converts the matrix to a bitmask integer. The flip function efficiently toggles bits corresponding to the cell and its neighbors. BFS explores all possible flips while avoiding repeated states. When a zero mask is reached, the algorithm returns the minimum number of flips.
Go Solution
package main
func minFlips(mat [][]int) int {
m, n := len(mat), len(mat[0])
start := 0
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if mat[i][j] == 1 {
start |= 1 << (i*n + j)
}
}
}
flip := func(mask int, r int, c int) int {
directions := [][2]int{{0,0},{0,1},{0,-1},{1,0},{-1,0}}
for _, d := range directions {
nr, nc := r + d[0], c + d[1]
if nr >= 0 && nr < m && nc >= 0 && nc < n {
mask ^= 1 << (nr*n + nc)
}
}
return mask
}
type pair struct {
mask, steps int
}
queue := []pair{{start, 0}}
seen := map[int]bool{start:true}
for len(queue) > 0 {
p := queue[0]
queue = queue[1:]
if p.mask == 0 {
return p.steps
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
newMask := flip(p.mask, i, j)
if !seen[newMask] {
seen[newMask] = true
queue = append(queue, pair{newMask, p.steps + 1})
}
}
}
}
return -1
}
Go uses slices as queues and maps for visited states. Bitmask operations and BFS logic mirror the Python version. Go does not require type hints, and we handle queues using slice operations.
Worked Examples
Example 1: mat = [[0,0],[0,1]]
Initial bitmask: 0001 (binary). BFS steps:
| Step | Mask | Flip action | New Mask |
|---|---|---|---|
| 0 | 0001 | flip(1,0) | 0111 |
| 1 | 0111 | flip(0,1) | 1011 |
| 2 | 1011 | flip(1,1) | 0000 |
Minimum flips: 3.
Example 2: mat = [[0]]
Bitmask: 0. Already zero, so result is 0.
Example 3: mat = [[1,0,0],[1,0,0]]
Bitmask: 100001. BFS explores all 64 possible states, none reach zero, so result is -1.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(2^(m_n) * m_n) | BFS explores each possible matrix state, for each state we consider m*n flips |
| Space | O(2^(m*n)) | Store all visited states in a set |
The exponential factor is acceptable because m*n <= 9. BFS guarantees minimal flips due to level-wise exploration, and bitmasking ensures efficient state transitions.
Test Cases
# Provided examples
assert Solution().minFlips([[0,0],[0,1]]) == 3 # multiple flips required
assert Solution().minFlips([[0]]) == 0 # already zero
assert Solution().minFlips([[1,0,0],[1,0,0]]) == -1 # impossible
# Boundary cases
assert Solution().minFlips([[1]]) == 1 # single cell flip
assert Solution().minFlips([[0,1],[1,0]]) == 4 # diagonal flips
assert Solution().minFlips([[1,1,1],[1,1,1],[1,1,1]]) == 5 # full 3x3 matrix
# Random small cases
assert Solution().minFlips([[1,0],[0,1]]) == 4
assert Solution().minFlips([[0,1,0],[