2679. Sum in a Matrix
Problem Description
You are given a 2D integer array nums
where each row represents a separate list of numbers. Starting with a score of 0, you need to perform operations until all numbers are removed from the matrix.
In each operation:
- Remove the largest number from each row - Go through every row and pick out the largest number from that row, removing it from the matrix. If there are multiple largest numbers in a row (ties), you can choose any of them.
- Add the maximum of removed numbers to score - Among all the numbers you just removed from different rows, find the highest one and add it to your running score.
You repeat these operations until no numbers remain in the matrix, then return the final score.
Example walkthrough:
For nums = [[7,2,1],[6,4,2],[6,5,3],[3,2,1]]
:
- Operation 1: Remove 7 from row 1, 6 from row 2, 6 from row 3, and 3 from row 4. The maximum is 7, so add 7 to score.
- Operation 2: Remove 2 from row 1, 4 from row 2, 5 from row 3, and 2 from row 4. The maximum is 5, so add 5 to score.
- Operation 3: Remove 1 from row 1, 2 from row 2, 3 from row 3, and 1 from row 4. The maximum is 3, so add 3 to score.
- Final score = 7 + 5 + 3 = 15
The solution approach sorts each row first, which arranges numbers from smallest to largest. Then by using zip(*nums)
, it transposes the matrix to group elements by their position in sorted rows. Taking the max
of each group gives the maximum value that would be selected in each operation, and summing these maximums gives the final score.
Intuition
The key insight is recognizing that we need to repeatedly select the largest element from each row, then take the maximum among those selected elements. This process continues until all elements are consumed.
Think about what happens if we sort each row first. After sorting, the largest element in each row will be at the rightmost position. In the first operation, we'd pick the last element from each row. In the second operation, we'd pick the second-to-last element from each row, and so on.
This reveals a pattern: after sorting each row, we're essentially picking elements column by column from right to left. The elements in the same column position (after sorting) will be selected together in the same operation.
For example, if we have sorted rows:
- Row 1:
[1, 2, 7]
- Row 2:
[2, 4, 6]
- Row 3:
[3, 5, 6]
- Row 4:
[1, 2, 3]
The last column [7, 6, 6, 3]
represents elements selected in operation 1, with maximum 7.
The middle column [2, 4, 5, 2]
represents elements selected in operation 2, with maximum 5.
The first column [1, 2, 3, 1]
represents elements selected in operation 3, with maximum 3.
This is where the transpose operation zip(*nums)
becomes brilliant - it groups elements by their column position, giving us exactly the groups of elements that would be selected together in each operation. We then just need to find the max
of each group and sum
them all to get our final score.
The solution elegantly transforms the row-wise selection problem into a column-wise maximum problem through sorting and transposition.
Learn more about Sorting and Heap (Priority Queue) patterns.
Solution Approach
The solution implements a clever transformation strategy that simplifies the problem:
Step 1: Sort each row
for row in nums: row.sort()
This sorts each row in ascending order in-place. After sorting, the largest elements are positioned at the end of each row, the second-largest elements are at the second-to-last position, and so on.
Step 2: Transpose and find column maximums
return sum(map(max, zip(*nums)))
This line does three things:
zip(*nums)
transposes the sorted matrix. The*
operator unpacks the rows, andzip
groups elements by their column index. For example, if we have 3 sorted rows[1,2,3]
,[4,5,6]
,[7,8,9]
, thenzip(*nums)
produces(1,4,7)
,(2,5,8)
,(3,6,9)
.map(max, ...)
applies themax
function to each tuple produced byzip
, finding the maximum element in each column.sum(...)
adds up all these maximum values to get the final score.
Why this works: After sorting, elements at the same column index represent numbers that would be selected together in the same operation (since we always pick the largest remaining element from each row). The rightmost column contains the largest elements from each row (selected in operation 1), the second-rightmost column contains the second-largest elements (selected in operation 2), and so on.
By transposing the sorted matrix with zip(*nums)
, we group these elements that would be selected together. Finding the max
of each group gives us the value added to the score in each operation, and summing these values gives us the final answer.
Time Complexity: O(m * n * log(n))
where m
is the number of rows and n
is the number of columns, dominated by sorting each row.
Space Complexity: O(1)
if we don't count the space used by zip
iterator, otherwise O(m * n)
for the transposed structure.
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 trace through a small example to illustrate the solution approach with nums = [[9,5],[8,7],[4,6]]
:
Initial Matrix:
Row 0: [9, 5] Row 1: [8, 7] Row 2: [4, 6]
Step 1: Sort each row in ascending order
Row 0: [5, 9] (5 moved to front, 9 to back) Row 1: [7, 8] (7 moved to front, 8 to back) Row 2: [4, 6] (already in order)
*Step 2: Transpose the sorted matrix using zip(nums)
After sorting, we can visualize the matrix columns:
Column 0: [5, 7, 4] (smallest elements from each row) Column 1: [9, 8, 6] (largest elements from each row)
The zip(*nums)
operation groups these columns:
- First tuple:
(5, 7, 4)
- elements that would be removed in operation 2 - Second tuple:
(9, 8, 6)
- elements that would be removed in operation 1
Step 3: Find maximum of each column and sum
max(5, 7, 4) = 7
(contribution from operation 2)max(9, 8, 6) = 9
(contribution from operation 1)- Total score:
7 + 9 = 16
Verification with original problem approach:
- Operation 1: Remove 9 from row 0, 8 from row 1, 6 from row 2 → max is 9, score = 9
- Operation 2: Remove 5 from row 0, 7 from row 1, 4 from row 2 → max is 7, score = 9 + 7 = 16
The sorted columns represent the reverse order of operations - the rightmost column (largest elements) is removed first, while the leftmost column (smallest elements) is removed last. The transpose elegantly groups elements that participate in the same operation together.
Solution Implementation
1class Solution:
2 def matrixSum(self, nums: List[List[int]]) -> int:
3 # Sort each row in ascending order
4 for row in nums:
5 row.sort()
6
7 # Transpose the matrix using zip(*nums) to group elements by column index
8 # Then find the maximum value in each column and sum them up
9 return sum(map(max, zip(*nums)))
10
1class Solution {
2 public int matrixSum(int[][] nums) {
3 // Sort each row in ascending order
4 for (int[] row : nums) {
5 Arrays.sort(row);
6 }
7
8 // Initialize the answer to accumulate the sum
9 int answer = 0;
10
11 // Iterate through each column index
12 for (int columnIndex = 0; columnIndex < nums[0].length; columnIndex++) {
13 // Find the maximum value in the current column
14 int maxInColumn = 0;
15 for (int[] row : nums) {
16 maxInColumn = Math.max(maxInColumn, row[columnIndex]);
17 }
18 // Add the maximum value of this column to the answer
19 answer += maxInColumn;
20 }
21
22 return answer;
23 }
24}
25
1class Solution {
2public:
3 int matrixSum(vector<vector<int>>& nums) {
4 // Sort each row in ascending order
5 for (auto& row : nums) {
6 sort(row.begin(), row.end());
7 }
8
9 int totalSum = 0;
10
11 // Iterate through each column index
12 for (int colIndex = 0; colIndex < nums[0].size(); ++colIndex) {
13 int maxInColumn = 0;
14
15 // Find the maximum value in the current column
16 for (const auto& row : nums) {
17 maxInColumn = max(maxInColumn, row[colIndex]);
18 }
19
20 // Add the maximum value of this column to the total sum
21 totalSum += maxInColumn;
22 }
23
24 return totalSum;
25 }
26};
27
1/**
2 * Calculates the sum of maximum elements in each column after sorting each row
3 * @param nums - 2D array of numbers to process
4 * @returns The sum of maximum values from each column
5 */
6function matrixSum(nums: number[][]): number {
7 // Sort each row in ascending order
8 for (const row of nums) {
9 row.sort((a: number, b: number) => a - b);
10 }
11
12 // Initialize the answer to accumulate the sum
13 let answer: number = 0;
14
15 // Iterate through each column index
16 for (let columnIndex: number = 0; columnIndex < nums[0].length; columnIndex++) {
17 // Track the maximum value in the current column
18 let maxInColumn: number = 0;
19
20 // Find the maximum value across all rows for the current column
21 for (const row of nums) {
22 maxInColumn = Math.max(maxInColumn, row[columnIndex]);
23 }
24
25 // Add the maximum value of this column to the answer
26 answer += maxInColumn;
27 }
28
29 return answer;
30}
31
Time and Space Complexity
Time Complexity: O(m * n * log(n))
where m
is the number of rows and n
is the number of columns in the matrix.
- The outer loop iterates through each row:
O(m)
iterations - For each row, we perform a sort operation:
O(n * log(n))
per row - Total sorting time:
O(m * n * log(n))
- The
zip(*nums)
operation transposes the matrix:O(m * n)
- The
map(max, ...)
operation finds the maximum of each column:O(m * n)
(finding max ofm
elements forn
columns) - The
sum()
operation:O(n)
- Overall time complexity is dominated by the sorting step:
O(m * n * log(n))
Space Complexity: O(m * n)
- The sorting is done in-place for each row, so no additional space is needed for sorting beyond the sorting algorithm's internal space which is typically
O(log(n))
for Python's Timsort - The
zip(*nums)
operation creates a transposed view of the matrix, which requiresO(m * n)
space to store all the transposed tuples - The
map()
creates an iterator with maximum values:O(n)
space - Overall space complexity:
O(m * n)
due to the transpose operation
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Assuming Non-Rectangular Input
The most common pitfall is assuming all rows have the same length. If the input matrix is jagged (rows have different lengths), zip(*nums)
will stop at the shortest row, potentially missing values from longer rows.
Example of the issue:
nums = [[7, 2, 1], [6, 4], [5]] # Jagged array # After sorting: [[1, 2, 7], [4, 6], [5]] # zip(*nums) only produces: (1, 4, 5) # Missing (2, 6) and (7,) groups!
Solution:
Use itertools.zip_longest
with a fill value:
from itertools import zip_longest
class Solution:
def matrixSum(self, nums: List[List[int]]) -> int:
for row in nums:
row.sort()
# Use zip_longest with fillvalue=0 to handle rows of different lengths
return sum(max(col) for col in zip_longest(*nums, fillvalue=0))
2. Modifying Input Unintentionally
The solution sorts rows in-place using row.sort()
, which modifies the original input. This could be problematic if the caller expects the input to remain unchanged.
Solution: Create a copy before sorting:
class Solution:
def matrixSum(self, nums: List[List[int]]) -> int:
# Create a sorted copy instead of modifying in-place
sorted_nums = [sorted(row) for row in nums]
return sum(map(max, zip(*sorted_nums)))
3. Memory Issues with Large Matrices
While zip(*nums)
creates an iterator in Python 3, converting it to process all columns at once (like using list(zip(*nums))
) could cause memory issues with very large matrices.
Solution: Process iteratively without creating intermediate collections:
class Solution:
def matrixSum(self, nums: List[List[int]]) -> int:
for row in nums:
row.sort()
total = 0
for col in zip(*nums):
total += max(col)
return total
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
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
Want a Structured Path to Master System Design Too? Don’t Miss This!