Facebook Pixel

1981. Minimize the Difference Between Target and Chosen Elements

Problem Description

You have a matrix mat with m rows and n columns containing integers, and you're given a target value target.

Your task is to select exactly one integer from each row of the matrix. After selecting one number from every row, you calculate the sum of all selected numbers. The goal is to make this sum as close as possible to the target value.

Specifically, you need to minimize the absolute difference between your sum and the target. The absolute difference between two numbers a and b is calculated as |a - b|.

For example, if you have a 3×3 matrix and need to pick one number from each row, you might pick the first element from row 1, the second element from row 2, and the third element from row 3. You would then sum these three numbers and find how far this sum is from your target.

The function should return the minimum possible absolute difference you can achieve by optimally selecting one element from each row.

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

Intuition

The key insight is that we need to explore all possible sums that can be formed by picking one element from each row. This is essentially a path-finding problem where each "path" represents a series of choices (one per row) and leads to a final sum.

We can think of this problem incrementally: after processing the first row, we know all possible sums we can achieve (which are just the elements in the first row). When we move to the second row, each previously achievable sum can be combined with each element in the second row to create new possible sums. This process continues row by row.

The approach uses dynamic programming with a set to track all achievable sums. Starting with f = {0} (representing no sum initially), we iterate through each row. For each row, we generate all new possible sums by adding each element in the current row to each previously achievable sum. This is captured in the expression set(a + b for a in f for b in row).

Why does this work? Because we're essentially building up all possible combinations systematically. If we can achieve a sum s using the first i rows, and row i+1 contains element x, then we can achieve sum s + x using the first i+1 rows.

After processing all rows, the set f contains all possible sums that can be achieved by selecting one element from each row. We then simply find which sum in this set has the minimum absolute difference from our target using min(abs(v - target) for v in f).

This approach is efficient because it avoids redundant calculations - if multiple paths lead to the same sum, we only store that sum once in our set.

Learn more about Dynamic Programming patterns.

Solution Approach

The solution implements a dynamic programming approach, specifically a variant of the grouped knapsack problem. Here's how the implementation works:

Data Structure: We use a set f to store all possible sums that can be achieved at each stage. Sets are ideal here because they automatically handle duplicates - if multiple combinations lead to the same sum, we only store it once.

Algorithm Steps:

  1. Initialize: Start with f = {0}, representing that before selecting any elements, the sum is 0.

  2. Iterate through rows: For each row in the matrix:

    • Generate all new possible sums by combining existing sums with elements from the current row
    • Update f with these new sums using: f = set(a + b for a in f for b in row)
    • This creates a Cartesian product between existing sums and current row elements
  3. Find minimum difference: After processing all rows, f contains all achievable sums. Calculate min(abs(v - target) for v in f) to find the sum closest to the target.

Dynamic Programming State: The state f[i][j] in the reference approach represents whether sum j is achievable using elements from the first i rows. The implementation optimizes this by:

  • Using a rolling array technique (keeping only the current state in set f)
  • Instead of a 2D boolean array, using a set to store only the achievable sums

Space Optimization: Rather than maintaining a full 2D DP table f[i][j], the solution uses a single set that gets updated row by row. This reduces space complexity while maintaining the same logic - if sum j was achievable with i-1 rows and we add element x from row i, then sum j+x becomes achievable with i rows.

Time Complexity: O(m × n × S) where m is the number of rows, n is the average number of elements per row, and S is the size of the set of possible sums.

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 walk through a small example to illustrate the solution approach.

Example Input:

  • Matrix mat = [[1, 2], [3, 4], [5, 6]] (3 rows × 2 columns)
  • Target target = 10

Goal: Select one element from each row to get a sum as close as possible to 10.

Step-by-step execution:

Initial State:

  • f = {0} (no elements selected yet, sum is 0)

Processing Row 1: [1, 2]

  • For each existing sum in f (currently just 0), add each element from row 1
  • New sums: 0 + 1 = 1 and 0 + 2 = 2
  • Update: f = {1, 2}
  • These represent: selecting 1 from row 1 OR selecting 2 from row 1

Processing Row 2: [3, 4]

  • For each sum in f = {1, 2}, add each element from row 2:
    • From sum 1: 1 + 3 = 4 and 1 + 4 = 5
    • From sum 2: 2 + 3 = 5 and 2 + 4 = 6
  • Update: f = {4, 5, 6}
  • These represent all possible sums after selecting from rows 1 and 2

Processing Row 3: [5, 6]

  • For each sum in f = {4, 5, 6}, add each element from row 3:
    • From sum 4: 4 + 5 = 9 and 4 + 6 = 10
    • From sum 5: 5 + 5 = 10 and 5 + 6 = 11
    • From sum 6: 6 + 5 = 11 and 6 + 6 = 12
  • Update: f = {9, 10, 11, 12}
  • These are all possible sums after selecting one element from each row

Finding the Answer:

  • Calculate absolute differences from target (10):
    • |9 - 10| = 1
    • |10 - 10| = 0 ← minimum!
    • |11 - 10| = 1
    • |12 - 10| = 2
  • Return: 0 (the minimum absolute difference)

Verification: The sum of 10 can be achieved by:

  • Selecting 1 from row 1, 3 from row 2, and 6 from row 3: 1 + 3 + 6 = 10
  • Or selecting 2 from row 1, 3 from row 2, and 5 from row 3: 2 + 3 + 5 = 10

Solution Implementation

1class Solution:
2    def minimizeTheDifference(self, mat: List[List[int]], target: int) -> int:
3        # Initialize a set to store all possible sums
4        # Starting with 0 as the base sum
5        possible_sums = {0}
6      
7        # Iterate through each row in the matrix
8        for row in mat:
9            # Generate new possible sums by adding each element in current row
10            # to each existing sum from previous rows
11            new_sums = set()
12            for current_sum in possible_sums:
13                for element in row:
14                    new_sums.add(current_sum + element)
15          
16            # Update possible_sums with the new set of sums
17            possible_sums = new_sums
18      
19        # Find the sum that minimizes the absolute difference with target
20        min_difference = min(abs(sum_value - target) for sum_value in possible_sums)
21      
22        return min_difference
23
1class Solution {
2    public int minimizeTheDifference(int[][] mat, int target) {
3        // Initialize DP array where dp[i] = true means sum i is achievable
4        // Initially, only sum 0 is achievable (by selecting nothing)
5        boolean[] dp = {true};
6      
7        // Process each row of the matrix
8        for (int[] currentRow : mat) {
9            // Find the maximum value in the current row
10            int maxValueInRow = 0;
11            for (int value : currentRow) {
12                maxValueInRow = Math.max(maxValueInRow, value);
13            }
14          
15            // Create new DP array with expanded size to accommodate new possible sums
16            // Size is increased by maxValueInRow to handle the largest possible new sum
17            boolean[] nextDp = new boolean[dp.length + maxValueInRow];
18          
19            // For each value in the current row
20            for (int value : currentRow) {
21                // Update all possible sums by adding current value
22                // Iterate through all positions where we can place this value
23                for (int sum = value; sum < dp.length + value; sum++) {
24                    // If (sum - value) was achievable in previous step,
25                    // then sum is achievable by adding current value
26                    nextDp[sum] |= dp[sum - value];
27                }
28            }
29          
30            // Move to the next iteration with updated DP array
31            dp = nextDp;
32        }
33      
34        // Find the achievable sum closest to the target
35        int minDifference = 1 << 30;  // Initialize with a large number (2^30)
36        for (int sum = 0; sum < dp.length; sum++) {
37            if (dp[sum]) {
38                // If this sum is achievable, calculate its difference from target
39                minDifference = Math.min(minDifference, Math.abs(sum - target));
40            }
41        }
42      
43        return minDifference;
44    }
45}
46
1class Solution {
2public:
3    int minimizeTheDifference(vector<vector<int>>& mat, int target) {
4        // dp[i] = 1 if sum i is achievable, 0 otherwise
5        // Initially, only sum 0 is achievable (empty selection)
6        vector<int> dp = {1};
7      
8        // Process each row of the matrix
9        for (auto& row : mat) {
10            // Find the maximum value in the current row
11            int maxValue = *max_element(row.begin(), row.end());
12          
13            // Create new DP array for the next state
14            // Size is extended by maxValue to accommodate new possible sums
15            vector<int> nextDp(dp.size() + maxValue);
16          
17            // For each element in the current row
18            for (int value : row) {
19                // Update all possible sums by adding current value
20                // j represents the new sum, j - value is the previous sum
21                for (int j = value; j < dp.size() + value; ++j) {
22                    // If sum (j - value) was achievable, then sum j is achievable
23                    nextDp[j] |= dp[j - value];
24                }
25            }
26          
27            // Move to the next state
28            dp = move(nextDp);
29        }
30      
31        // Find the achievable sum closest to target
32        int minDifference = 1 << 30;  // Initialize with a large value
33        for (int sum = 0; sum < dp.size(); ++sum) {
34            // If this sum is achievable
35            if (dp[sum]) {
36                // Update minimum difference
37                minDifference = min(minDifference, abs(sum - target));
38            }
39        }
40      
41        return minDifference;
42    }
43};
44
1function minimizeTheDifference(mat: number[][], target: number): number {
2    // dp[i] = 1 if sum i is achievable, 0 otherwise
3    // Initially, only sum 0 is achievable (empty selection)
4    let dp: number[] = [1];
5  
6    // Process each row of the matrix
7    for (const row of mat) {
8        // Find the maximum value in the current row
9        const maxValue = Math.max(...row);
10      
11        // Create new DP array for the next state
12        // Size is extended by maxValue to accommodate new possible sums
13        const nextDp: number[] = new Array(dp.length + maxValue).fill(0);
14      
15        // For each element in the current row
16        for (const value of row) {
17            // Update all possible sums by adding current value
18            // j represents the new sum, j - value is the previous sum
19            for (let j = value; j < dp.length + value; j++) {
20                // If sum (j - value) was achievable, then sum j is achievable
21                nextDp[j] = nextDp[j] | dp[j - value];
22            }
23        }
24      
25        // Move to the next state
26        dp = nextDp;
27    }
28  
29    // Find the achievable sum closest to target
30    let minDifference = 1 << 30;  // Initialize with a large value
31    for (let sum = 0; sum < dp.length; sum++) {
32        // If this sum is achievable
33        if (dp[sum]) {
34            // Update minimum difference
35            minDifference = Math.min(minDifference, Math.abs(sum - target));
36        }
37    }
38  
39    return minDifference;
40}
41

Time and Space Complexity

The time complexity is O(m^2 × n × C) where:

  • m is the number of rows in the matrix
  • n is the number of columns in the matrix
  • C is the maximum value of elements in the matrix

This complexity arises because:

  1. We iterate through m rows
  2. For each row, we generate a new set by combining each element in the current set f with each element in the current row
  3. The size of set f can grow up to O(m × C) in the worst case (since after processing k rows, the maximum possible sum is k × C)
  4. For each row, we perform |f| × n operations, which is O(m × C × n) in the worst case
  5. Since we do this for m rows, the total complexity is O(m × m × C × n) = O(m^2 × n × C)

The space complexity is O(m × C) because:

  • The set f stores all possible sums
  • After processing all m rows, the maximum possible sum is m × C
  • In the worst case, we might have O(m × C) distinct sums stored in the set
  • The temporary set created during each iteration also takes at most O(m × C) space

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Memory Limit Exceeded Due to Unbounded Set Growth

The Problem: The set of possible sums can grow exponentially. With m rows and n elements per row, theoretically we could have up to n^m different sums. For large matrices with diverse values, the set possible_sums can consume excessive memory, leading to Memory Limit Exceeded (MLE) errors.

Example Scenario: Consider a matrix with 70 rows where each row contains [1, 2, 3, ..., 70]. The number of possible sums grows rapidly, potentially creating millions of unique values in the set.

Solution - Pruning Strategy: Since we're looking for sums close to the target, we can prune sums that are already far from the target and unlikely to produce better results:

class Solution:
    def minimizeTheDifference(self, mat: List[List[int]], target: int) -> int:
        possible_sums = {0}
      
        for row in mat:
            new_sums = set()
            for current_sum in possible_sums:
                for element in row:
                    new_sums.add(current_sum + element)
          
            # Pruning: Keep only relevant sums
            if len(new_sums) > 5000:  # Threshold to prevent memory issues
                # Sort sums and keep those closest to target
                sorted_sums = sorted(new_sums, key=lambda x: abs(x - target))
                new_sums = set(sorted_sums[:5000])
          
            # Alternative pruning: Remove sums that are too far from target
            # if we already have a sum equal to or very close to target
            if target in new_sums:
                # Keep sums within a reasonable range of target
                new_sums = {s for s in new_sums if abs(s - target) <= 100}
          
            possible_sums = new_sums
      
        return min(abs(sum_value - target) for sum_value in possible_sums)

2. Early Termination Optimization Missing

The Problem: The original solution continues processing even after finding an exact match (sum equal to target), which is unnecessary since the minimum difference would be 0.

Solution - Early Exit:

class Solution:
    def minimizeTheDifference(self, mat: List[List[int]], target: int) -> int:
        possible_sums = {0}
      
        for row in mat:
            new_sums = set()
            for current_sum in possible_sums:
                for element in row:
                    new_sum = current_sum + element
                    new_sums.add(new_sum)
                  
                    # Early termination if we hit the target exactly
                    if new_sum == target:
                        # Check if this is the last row
                        if row is mat[-1]:
                            return 0
          
            possible_sums = new_sums
          
            # Check if target is achievable after processing this row
            if target in possible_sums:
                # Calculate minimum remaining rows contribution
                remaining_rows = len(mat) - mat.index(row) - 1
                if remaining_rows == 0:
                    return 0
      
        return min(abs(sum_value - target) for sum_value in possible_sums)

3. Optimization for Large Targets

The Problem: When the target is very large compared to matrix values, keeping track of all small sums wastes memory when we know they'll result in large differences.

Solution - Bounded Tracking:

class Solution:
    def minimizeTheDifference(self, mat: List[List[int]], target: int) -> int:
        possible_sums = {0}
        min_possible = sum(min(row) for row in mat)
        max_possible = sum(max(row) for row in mat)
      
        # If target is outside possible range, calculate directly
        if target <= min_possible:
            return min_possible - target
        if target >= max_possible:
            return target - max_possible
      
        for row in mat:
            new_sums = set()
            for current_sum in possible_sums:
                for element in row:
                    new_sum = current_sum + element
                    # Only keep sums that could potentially improve our answer
                    # Skip sums that are already too far from target
                    if new_sum <= target * 2:  # Heuristic boundary
                        new_sums.add(new_sum)
          
            possible_sums = new_sums
      
        return min(abs(sum_value - target) for sum_value in possible_sums)

These optimizations help prevent common runtime and memory issues while maintaining correctness of the solution.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement priority queue?


Recommended Readings

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

Load More