1253. Reconstruct a 2-Row Binary Matrix
Problem Description
You need to reconstruct a 2Γn binary matrix based on given constraints about its row sums and column sums.
You're given:
upper
: the sum of all elements in the first (upper) rowlower
: the sum of all elements in the second (lower) rowcolsum
: an array of lengthn
wherecolsum[i]
represents the sum of elements in columni
The matrix you construct must be binary (containing only 0s and 1s) and must satisfy all three constraints:
- The sum of the first row equals
upper
- The sum of the second row equals
lower
- For each column
i
, the sum of its two elements equalscolsum[i]
Since each column has only 2 elements and the matrix is binary, colsum[i]
can only be 0, 1, or 2:
- If
colsum[i] = 0
, both elements in columni
must be 0 - If
colsum[i] = 1
, exactly one element in columni
must be 1 - If
colsum[i] = 2
, both elements in columni
must be 1
Return any valid 2Γn matrix that satisfies all constraints. If no valid solution exists, return an empty 2-D array.
For example, if upper = 2
, lower = 1
, and colsum = [1, 1, 1]
, one valid answer would be [[1, 1, 0], [0, 0, 1]]
because the first row sums to 2, the second row sums to 1, and each column sums to 1.
Intuition
The key insight is that we can build the matrix column by column, making greedy decisions based on the column sum constraints.
For each column, we know exactly how many 1s it needs based on colsum[i]
. The question is just where to place them:
-
When
colsum[i] = 2
, we have no choice - both rows must have a 1 in this column. This uses up one 1 from bothupper
andlower
counts. -
When
colsum[i] = 0
, we have no choice either - both rows must have a 0 in this column. -
When
colsum[i] = 1
, we have flexibility - we can place the 1 in either row. This is where the greedy strategy comes in.
The greedy choice for colsum[i] = 1
is to place the 1 in whichever row still needs more 1s. If upper > lower
, we place it in the upper row; otherwise, in the lower row. This helps balance out the remaining 1s we need to place.
Why does this greedy approach work? Because we're not creating any future constraints that could make the problem unsolvable. Each column is independent - as long as we have enough 1s left to distribute (upper
and lower
don't go negative), and we end up using exactly the right number of 1s for each row (both upper
and lower
reach 0), we'll have a valid solution.
The algorithm fails and returns an empty array in two cases:
- During construction, if
upper
orlower
becomes negative (we've placed too many 1s in that row) - After construction, if
upper
orlower
is not exactly 0 (we haven't placed enough 1s in that row)
Learn more about Greedy patterns.
Solution Approach
We implement a greedy algorithm that constructs the matrix column by column:
-
Initialize the answer matrix: Create a 2Γn matrix
ans
filled with zeros. This will store our result withans[0]
representing the upper row andans[1]
representing the lower row. -
Process each column: Iterate through the
colsum
array. For each columnj
with sum valuev
:-
Case 1:
v = 2
: Both cells in this column must be 1.ans[0][j] = ans[1][j] = 1 upper, lower = upper - 1, lower - 1
We set both rows to 1 and decrement both
upper
andlower
counters. -
Case 2:
v = 1
: Exactly one cell in this column must be 1.if upper > lower: upper -= 1 ans[0][j] = 1 else: lower -= 1 ans[1][j] = 1
We use a greedy strategy: place the 1 in whichever row needs more 1s. If
upper > lower
, place it in the upper row; otherwise, place it in the lower row. -
Case 3:
v = 0
: Both cells remain 0 (already initialized), so no action needed.
-
-
Validity check during construction: After processing each column, check if
upper < 0
orlower < 0
. If either becomes negative, it means we've placed more 1s in that row than allowed, so return an empty array immediately. -
Final validation: After processing all columns, verify that both
upper
andlower
equal exactly 0. This ensures we've placed exactly the right number of 1s in each row. If both are 0, returnans
; otherwise, return an empty array.
The time complexity is O(n)
where n
is the number of columns, as we process each column once. The space complexity is O(n)
for storing the answer matrix (not counting the output space).
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through an example with upper = 3
, lower = 2
, and colsum = [2, 1, 1, 0, 1]
.
We need to construct a 2Γ5 binary matrix where:
- The upper row sums to 3
- The lower row sums to 2
- Each column i sums to colsum[i]
Step 1: Initialize
- Create a 2Γ5 matrix filled with zeros:
[[0,0,0,0,0], [0,0,0,0,0]]
- Track remaining 1s needed:
upper = 3
,lower = 2
Step 2: Process column 0 (colsum[0] = 2)
- Both cells must be 1
- Set
ans[0][0] = 1
andans[1][0] = 1
- Update counters:
upper = 2
,lower = 1
- Matrix:
[[1,0,0,0,0], [1,0,0,0,0]]
Step 3: Process column 1 (colsum[1] = 1)
- Exactly one cell must be 1
- Compare:
upper = 2 > lower = 1
, so place 1 in upper row - Set
ans[0][1] = 1
- Update:
upper = 1
- Matrix:
[[1,1,0,0,0], [1,0,0,0,0]]
Step 4: Process column 2 (colsum[2] = 1)
- Exactly one cell must be 1
- Compare:
upper = 1 = lower = 1
, so place 1 in lower row (tie broken by else condition) - Set
ans[1][2] = 1
- Update:
lower = 0
- Matrix:
[[1,1,0,0,0], [1,0,1,0,0]]
Step 5: Process column 3 (colsum[3] = 0)
- Both cells remain 0 (no change needed)
- Matrix:
[[1,1,0,0,0], [1,0,1,0,0]]
Step 6: Process column 4 (colsum[4] = 1)
- Exactly one cell must be 1
- Compare:
upper = 1 > lower = 0
, so place 1 in upper row - Set
ans[0][4] = 1
- Update:
upper = 0
- Matrix:
[[1,1,0,0,1], [1,0,1,0,0]]
Step 7: Final validation
- Check:
upper = 0
β andlower = 0
β - Both are exactly 0, so we have a valid solution
Result: [[1,1,0,0,1], [1,0,1,0,0]]
Let's verify:
- Upper row sum: 1+1+0+0+1 = 3 β
- Lower row sum: 1+0+1+0+0 = 2 β
- Column sums: [2,1,1,0,1] β
The greedy strategy successfully distributed the 1s to satisfy all constraints!
Solution Implementation
1class Solution:
2 def reconstructMatrix(
3 self, upper: int, lower: int, colsum: List[int]
4 ) -> List[List[int]]:
5 """
6 Reconstruct a 2x n binary matrix from given row and column sums.
7
8 Args:
9 upper: Sum of elements in the first row
10 lower: Sum of elements in the second row
11 colsum: List where colsum[j] is the sum of elements in column j
12
13 Returns:
14 A valid 2x n binary matrix if possible, otherwise empty list
15 """
16 n = len(colsum)
17 # Initialize a 2x n matrix filled with zeros
18 matrix = [[0] * n for _ in range(2)]
19
20 # Track remaining sums for upper and lower rows
21 upper_remaining = upper
22 lower_remaining = lower
23
24 # Process each column based on its sum requirement
25 for col_idx, col_sum in enumerate(colsum):
26 if col_sum == 2:
27 # Column sum is 2: both rows must have 1 in this column
28 matrix[0][col_idx] = 1
29 matrix[1][col_idx] = 1
30 upper_remaining -= 1
31 lower_remaining -= 1
32
33 elif col_sum == 1:
34 # Column sum is 1: exactly one row should have 1 in this column
35 # Greedily assign to the row with more remaining sum
36 if upper_remaining > lower_remaining:
37 matrix[0][col_idx] = 1
38 upper_remaining -= 1
39 else:
40 matrix[1][col_idx] = 1
41 lower_remaining -= 1
42
43 # Check if we've exceeded the row sum constraints
44 if upper_remaining < 0 or lower_remaining < 0:
45 return []
46
47 # Verify that we've used exactly the required sums for both rows
48 if upper_remaining == 0 and lower_remaining == 0:
49 return matrix
50 else:
51 return []
52
1class Solution {
2 public List<List<Integer>> reconstructMatrix(int upper, int lower, int[] colsum) {
3 int numColumns = colsum.length;
4 List<Integer> upperRow = new ArrayList<>();
5 List<Integer> lowerRow = new ArrayList<>();
6
7 // Iterate through each column to construct the matrix
8 for (int col = 0; col < numColumns; col++) {
9 int upperValue = 0;
10 int lowerValue = 0;
11
12 // If column sum is 2, both rows must have 1
13 if (colsum[col] == 2) {
14 upperValue = 1;
15 lowerValue = 1;
16 upper--;
17 lower--;
18 }
19 // If column sum is 1, choose which row gets the 1
20 else if (colsum[col] == 1) {
21 // Greedily assign 1 to the row with more remaining sum
22 // This helps balance the distribution
23 if (upper > lower) {
24 upperValue = 1;
25 upper--;
26 } else {
27 lowerValue = 1;
28 lower--;
29 }
30 }
31 // If column sum is 0, both values remain 0 (already initialized)
32
33 // Check if we've exceeded the row sum constraints
34 if (upper < 0 || lower < 0) {
35 break;
36 }
37
38 // Add the determined values to respective rows
39 upperRow.add(upperValue);
40 lowerRow.add(lowerValue);
41 }
42
43 // Return the reconstructed matrix if valid, otherwise return empty list
44 // Valid means we've used exactly the required sums for both rows
45 return (upper == 0 && lower == 0) ? List.of(upperRow, lowerRow) : List.of();
46 }
47}
48
1class Solution {
2public:
3 vector<vector<int>> reconstructMatrix(int upper, int lower, vector<int>& colsum) {
4 int numColumns = colsum.size();
5 // Initialize a 2x n matrix with all zeros
6 vector<vector<int>> resultMatrix(2, vector<int>(numColumns, 0));
7
8 // Process each column based on its sum requirement
9 for (int col = 0; col < numColumns; ++col) {
10 // Case 1: Column sum is 2 - both rows must have 1
11 if (colsum[col] == 2) {
12 resultMatrix[0][col] = 1;
13 resultMatrix[1][col] = 1;
14 upper--; // Decrement upper row sum
15 lower--; // Decrement lower row sum
16 }
17 // Case 2: Column sum is 1 - exactly one row should have 1
18 else if (colsum[col] == 1) {
19 // Greedily assign 1 to the row with more remaining sum
20 if (upper > lower) {
21 resultMatrix[0][col] = 1;
22 upper--;
23 } else {
24 resultMatrix[1][col] = 1;
25 lower--;
26 }
27 }
28 // Case 3: Column sum is 0 - both rows remain 0 (already initialized)
29
30 // Early termination: If either sum becomes negative, matrix is impossible
31 if (upper < 0 || lower < 0) {
32 break;
33 }
34 }
35
36 // Check if the exact row sums were achieved
37 // Return empty matrix if constraints cannot be satisfied
38 return (upper == 0 && lower == 0) ? resultMatrix : vector<vector<int>>();
39 }
40};
41
1/**
2 * Reconstructs a 2x n binary matrix from given row and column sums
3 * @param upper - Sum of elements in the first row
4 * @param lower - Sum of elements in the second row
5 * @param colsum - Array where colsum[i] is the sum of elements in column i
6 * @returns 2D binary matrix if valid reconstruction exists, empty array otherwise
7 */
8function reconstructMatrix(upper: number, lower: number, colsum: number[]): number[][] {
9 const columnCount: number = colsum.length;
10
11 // Initialize a 2 x n matrix filled with zeros
12 const resultMatrix: number[][] = Array(2)
13 .fill(0)
14 .map(() => Array(columnCount).fill(0));
15
16 // Remaining count for upper and lower rows
17 let upperRemaining: number = upper;
18 let lowerRemaining: number = lower;
19
20 // Process each column based on its sum requirement
21 for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
22 if (colsum[columnIndex] === 2) {
23 // Column sum is 2: both rows must have 1
24 resultMatrix[0][columnIndex] = 1;
25 resultMatrix[1][columnIndex] = 1;
26 upperRemaining--;
27 lowerRemaining--;
28 } else if (colsum[columnIndex] === 1) {
29 // Column sum is 1: choose which row gets the 1
30 // Greedily assign to the row with more remaining count
31 if (upperRemaining > lowerRemaining) {
32 resultMatrix[0][columnIndex] = 1;
33 upperRemaining--;
34 } else {
35 resultMatrix[1][columnIndex] = 1;
36 lowerRemaining--;
37 }
38 }
39 // Column sum is 0: both rows remain 0 (already initialized)
40
41 // Early termination if constraints become impossible
42 if (upperRemaining < 0 || lowerRemaining < 0) {
43 break;
44 }
45 }
46
47 // Check if all row sum constraints are satisfied
48 // Return empty array if not, otherwise return the constructed matrix
49 return (upperRemaining !== 0 || lowerRemaining !== 0) ? [] : resultMatrix;
50}
51
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the array colsum
. The algorithm iterates through the colsum
array exactly once using a single for loop with enumerate()
. Inside the loop, all operations (comparisons, assignments, and arithmetic operations) are performed in constant time O(1)
. Therefore, the overall time complexity is O(n)
.
Space Complexity: O(1)
(excluding the output array). The algorithm uses only a constant amount of extra space for variables like n
, j
, and v
. The answer array ans
of size 2 Γ n
is required for the output and is not counted towards the space complexity as per the reference. If we were to include the output array, the space complexity would be O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Greedy Strategy for Column Sum = 1
The Pitfall: When colsum[i] = 1
, the code uses a greedy approach that places the 1 in whichever row has more remaining sum (upper_remaining > lower_remaining
). However, this strategy can fail in certain cases where it makes locally optimal choices that prevent a globally valid solution.
Example of Failure:
upper = 4, lower = 2, colsum = [2, 1, 1, 1, 1]
Following the current greedy approach:
- Column 0 (sum=2): Both rows get 1,
upper_remaining=3
,lower_remaining=1
- Column 1 (sum=1): Since 3 > 1, upper row gets 1,
upper_remaining=2
,lower_remaining=1
- Column 2 (sum=1): Since 2 > 1, upper row gets 1,
upper_remaining=1
,lower_remaining=1
- Column 3 (sum=1): Since 1 == 1, lower row gets 1,
upper_remaining=1
,lower_remaining=0
- Column 4 (sum=1): Since 1 > 0, upper row gets 1,
upper_remaining=0
,lower_remaining=0
Result: [[1,1,1,0,1], [1,0,0,1,0]]
- This works, but only by chance.
Better Example Showing the Issue: Consider if we need to place 1s more strategically based on future columns with sum=2.
2. Not Pre-validating Total Sum Constraints
The Pitfall: The code doesn't check upfront if the problem is solvable. It discovers invalidity during construction, which is inefficient.
The Problem: Before starting construction, we should verify:
- The sum of all
colsum
values equalsupper + lower
- We have enough columns with sum=2 and sum=1 to satisfy both row constraints
Solution - Improved Code:
class Solution:
def reconstructMatrix(
self, upper: int, lower: int, colsum: List[int]
) -> List[List[int]]:
n = len(colsum)
# Pre-validation: Check if total sum matches
total_sum = sum(colsum)
if total_sum != upper + lower:
return []
# Count columns by their sum values
count_2 = sum(1 for x in colsum if x == 2)
count_1 = sum(1 for x in colsum if x == 1)
# Check if it's possible to distribute 1s
# Columns with sum=2 contribute 1 to both rows
# After placing these, we need to distribute remaining 1s
upper_needed = upper - count_2
lower_needed = lower - count_2
# Check if we have negative needs or insufficient columns with sum=1
if upper_needed < 0 or lower_needed < 0:
return []
if upper_needed + lower_needed != count_1:
return []
# Now construct the matrix
matrix = [[0] * n for _ in range(2)]
upper_remaining = upper_needed # 1s still needed for upper row (excluding sum=2 columns)
for col_idx, col_sum in enumerate(colsum):
if col_sum == 2:
matrix[0][col_idx] = 1
matrix[1][col_idx] = 1
elif col_sum == 1:
# Place 1 in upper row if we still need 1s there
if upper_remaining > 0:
matrix[0][col_idx] = 1
upper_remaining -= 1
else:
matrix[1][col_idx] = 1
return matrix
Key Improvements:
- Pre-validation: Checks total sum constraint and feasibility before construction
- Simplified greedy logic: Instead of comparing remaining sums, we simply fill the upper row first until it reaches its quota, then fill the lower row
- Early termination: Returns empty array immediately if constraints can't be satisfied
- More predictable behavior: The solution is deterministic and easier to reason about
Depth first search is equivalent to which of the tree traversal order?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!