LeetCode 1253 - Reconstruct a 2-Row Binary Matrix
The problem asks us to reconstruct a binary matrix with exactly two rows and n columns given three constraints: the sum
Difficulty: 🟡 Medium
Topics: Array, Greedy, Matrix
Solution
Problem Understanding
The problem asks us to reconstruct a binary matrix with exactly two rows and n columns given three constraints: the sum of elements in the upper row (upper), the sum of elements in the lower row (lower), and the sum of elements in each column (colsum). Each element in the matrix is either 0 or 1. The colsum[i] represents the total number of 1s in the i-th column. If colsum[i] is 2, both rows must have a 1 in that column; if it is 1, either the upper or lower row has a 1; if it is 0, both rows have 0.
The input constraints indicate that the number of columns can be very large, up to 10^5, which rules out naive approaches that try every combination. Additionally, upper and lower are bounded by the number of columns, so any reconstruction must not exceed these sums. The problem guarantees that colsum values are only 0, 1, or 2, simplifying the decision logic.
Key edge cases include scenarios where upper or lower cannot accommodate the required number of 1s, when colsum contains multiple 2s exceeding the row sums, and when colsum length is minimal (1) or maximal (10^5).
Approaches
A brute-force approach would attempt to generate all possible combinations of 0 and 1 placements for each column that satisfy colsum[i], and then check if the resulting rows sum to upper and lower. This method is correct in principle but computationally infeasible because for n columns, there are up to 2^n combinations for columns where colsum[i] == 1.
The optimal approach relies on a greedy strategy and the observation that the columns with colsum[i] == 2 are fixed: both rows must have 1 there. Columns with colsum[i] == 1 can be assigned to either the upper or lower row, prioritizing whichever row has remaining sum capacity. By iterating through the columns in order and assigning 1s greedily while decrementing upper and lower, we can efficiently reconstruct the matrix or detect that it is impossible.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(2^n) | O(n) | Attempts all combinations of placements for columns with colsum[i] == 1 |
| Optimal | O(n) | O(n) | Greedily assign 1s based on colsum[i] values and remaining upper and lower sums |
Algorithm Walkthrough
- Initialize two lists
upper_rowandlower_rowwith zeros of lengthnto represent the two rows. - Iterate through
colsumfrom index0ton-1. - If
colsum[i] == 2, assign1to bothupper_row[i]andlower_row[i]. Decrement bothupperandlowerby1. If eitherupperorlowerbecomes negative, return an empty matrix as it is impossible. - If
colsum[i] == 1, assign1to the row (upper or lower) that still has remaining sum capacity. Prefer the upper row first ifupper > 0. Decrement the corresponding row sum. If bothupperandlowerare zero when a1is needed, return an empty matrix. - If
colsum[i] == 0, leave bothupper_row[i]andlower_row[i]as0. - After processing all columns, if
upperorloweris not zero, return an empty matrix because the row sums do not match the given constraints. - Return
[upper_row, lower_row].
Why it works: The invariant is that after processing each column, the remaining upper and lower counts reflect the number of 1s we still need to place. Columns with 2 must be 1 in both rows, and columns with 1 are placed greedily in a row with remaining capacity. This ensures that no constraints are violated while respecting the sum requirements.
Python Solution
from typing import List
class Solution:
def reconstructMatrix(self, upper: int, lower: int, colsum: List[int]) -> List[List[int]]:
n = len(colsum)
upper_row = [0] * n
lower_row = [0] * n
for i in range(n):
if colsum[i] == 2:
if upper <= 0 or lower <= 0:
return []
upper_row[i] = 1
lower_row[i] = 1
upper -= 1
lower -= 1
elif colsum[i] == 1:
if upper > 0:
upper_row[i] = 1
upper -= 1
elif lower > 0:
lower_row[i] = 1
lower -= 1
else:
return []
if upper != 0 or lower != 0:
return []
return [upper_row, lower_row]
In this implementation, we initialize two rows with zeros and iterate through colsum. We handle the fixed case colsum[i] == 2 first, decrementing the sums. For colsum[i] == 1, we assign 1 to whichever row can still accommodate it. If the sums mismatch at the end or a placement is impossible, we return an empty list.
Go Solution
func reconstructMatrix(upper int, lower int, colsum []int) [][]int {
n := len(colsum)
upperRow := make([]int, n)
lowerRow := make([]int, n)
for i := 0; i < n; i++ {
if colsum[i] == 2 {
if upper <= 0 || lower <= 0 {
return [][]int{}
}
upperRow[i] = 1
lowerRow[i] = 1
upper--
lower--
} else if colsum[i] == 1 {
if upper > 0 {
upperRow[i] = 1
upper--
} else if lower > 0 {
lowerRow[i] = 1
lower--
} else {
return [][]int{}
}
}
}
if upper != 0 || lower != 0 {
return [][]int{}
}
return [][]int{upperRow, lowerRow}
}
In Go, we use slices to represent the rows and carefully handle nil versus empty slice returns. Logic mirrors Python, with explicit decrements and checks for feasibility.
Worked Examples
Example 1: upper = 2, lower = 1, colsum = [1,1,1]
| i | colsum[i] | upper_row | lower_row | upper | lower |
|---|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 0 | 0 | 1 |
| 2 | 1 | 0 | 1 | 0 | 0 |
Output: [[1,1,0],[0,0,1]]
Example 2: upper = 2, lower = 3, colsum = [2,2,1,1]
First column colsum[0] == 2 requires decrementing upper and lower by 1. After second column colsum[1] == 2, upper becomes 0, lower becomes 1. For colsum[2] == 1, upper is 0, assign 1 to lower. For colsum[3] == 1, upper is 0, lower is 0, impossible. Output: [].
Example 3: upper = 5, lower = 5, colsum = [2,1,2,0,1,0,1,2,0,1]
Following greedy allocation based on remaining upper and lower sums produces the output matrix:
[[1,1,1,0,1,0,0,1,0,0],[1,0,1,0,0,0,1,1,0,1]].
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Single pass through colsum with constant work per column |
| Space | O(n) | Two additional rows of length n are stored |
The greedy algorithm ensures we only iterate once, and the matrix output itself requires O(n) space.
Test Cases
# Provided examples
assert Solution().reconstructMatrix(2, 1, [1,1,1]) == [[1,1,0],[0,0,1]] # multiple correct outputs possible
assert Solution().reconstructMatrix(2, 3, [2,2,1,1]) == [] # impossible
assert Solution().reconstructMatrix(5, 5, [2,1,2,0,1,0,1,2,0,1]) == [[1,1