Facebook Pixel

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:

  1. 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.
  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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, and zip groups elements by their column index. For example, if we have 3 sorted rows [1,2,3], [4,5,6], [7,8,9], then zip(*nums) produces (1,4,7), (2,5,8), (3,6,9).
  • map(max, ...) applies the max function to each tuple produced by zip, 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 Evaluator

Example 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 of m elements for n 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 requires O(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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More