Magicsheet logo

Maximum Number of Points with Cost

Medium
12.5%
Updated 8/1/2025

Maximum Number of Points with Cost

What is this problem about?

The Maximum Number of Points with Cost coding problem gives you a 2D grid points where points[r][c] represents the points gained by choosing cell (r,c). You start by choosing any cell in the first row. For each subsequent row r+1, you must choose a cell (r+1, c2) such that points[r+1][c2] is added to your score, but with a penalty of abs(c1 - c2) where c1 was the chosen column in row r. You want to maximize your total score.

Why is this asked in interviews?

Microsoft, DE Shaw, Amazon, Google, and Bloomberg use this problem to test dynamic programming with optimizations that avoid O(N*M^2) complexity. The abs(c1 - c2) penalty suggests a need for monotonic queue optimization or prefix/suffix maximums to reduce the transition cost from O(M) to O(1).

Algorithmic pattern used

Dynamic Programming with Optimization (Monotonic Queue or Prefix/Suffix Maximums): Let dp[r][c] be the maximum score achievable ending at cell (r, c). The recurrence relation is: dp[r][c] = points[r][c] + max(dp[r-1][prev_c] - abs(c - prev_c)) for all 0 <= prev_c < M. A naive calculation of max(...) for each dp[r][c] takes O(M) time, leading to O(N*M^2) total, which is too slow for large N, M.

Optimization: The dp[r-1][prev_c] - abs(c - prev_c) term can be rewritten as: dp[r-1][prev_c] - (c - prev_c) if prev_c <= c (which is dp[r-1][prev_c] + prev_c - c) dp[r-1][prev_c] - (prev_c - c) if prev_c > c (which is dp[r-1][prev_c] - prev_c + c)

Notice that c is constant for a fixed dp[r][c] calculation. We need to maximize:

  1. dp[r-1][prev_c] + prev_c for prev_c <= c
  2. dp[r-1][prev_c] - prev_c for prev_c > c

This can be done in O(M) for each row using prefix maximums for the first case and suffix maximums for the second case. For each row r:

  • Compute left_max[c] = max(left_max[c-1], dp[r-1][c] + c) (from left to right)
  • Compute right_max[c] = max(right_max[c+1], dp[r-1][c] - c) (from right to left)
  • Then dp[r][c] = points[r][c] + max(left_max[c] - c, right_max[c] + c).

The overall time complexity becomes O(N*M).

Example explanation

Points grid:

[[1,2,3],
 [1,5,1],
 [3,1,1]]

N=3, M=3.

Row 0 (base case): dp[0] = [1, 2, 3] (max from first row is just the value itself)

Row 1: dp[1][0] = points[1][0] + max(dp[0][0]-abs(0-0), dp[0][1]-abs(0-1), dp[0][2]-abs(0-2)) = 1 + max(1-0, 2-1, 3-2) = 1 + max(1,1,1) = 1 + 1 = 2

Using optimized calculation for row 1: prev_dp = [1, 2, 3] left_values = [dp[0][0]+0, dp[0][1]+1, dp[0][2]+2] = [1, 3, 5] left_prefix_max = [1, 3, 5]

right_values = [dp[0][0]-0, dp[0][1]-1, dp[0][2]-2] = [1, 1, 1] right_suffix_max = [5, 1, 1] (recomputed from right to left)

Correct prefix/suffix max computation for row 0: left_max_values = [] current_max = -infinity for c from 0 to M-1: current_max = max(current_max, dp[0][c] + c) left_max_values.append(current_max) left_max_values = [1, 3, 5] (since dp[0] = [1,2,3])

right_max_values = [] current_max = -infinity for c from M-1 down to 0: current_max = max(current_max, dp[0][c] - c) right_max_values.insert(0, current_max) right_max_values = [1, 1, 1] (since dp[0] = [1,2,3])

Revised Calculation with Prefix/Suffix max on dp[r-1] for dp[r] values: Row 0: dp[0] = [1, 2, 3]

Row 1 calculation: prev_row_dp = [1, 2, 3] left_prefix = [-inf] * M right_suffix = [-inf] * M

left_prefix[0] = prev_row_dp[0] + 0 = 1 for c = 1 to M-1: left_prefix[c] = max(left_prefix[c-1], prev_row_dp[c] + c) left_prefix = [1, 3, 5]

right_suffix[M-1] = prev_row_dp[M-1] - (M-1) = 3 - 2 = 1 for c = M-2 down to 0: right_suffix[c] = max(right_suffix[c+1], prev_row_dp[c] - c) right_suffix = [1, 1, 1]

dp[1][0] = points[1][0] + max(left_prefix[0] - 0, right_suffix[0] + 0) = 1 + max(1, 1) = 1 + 1 = 2 dp[1][1] = points[1][1] + max(left_prefix[1] - 1, right_suffix[1] + 1) = 5 + max(3 - 1, 1 + 1) = 5 + max(2, 2) = 5 + 2 = 7 dp[1][2] = points[1][2] + max(left_prefix[2] - 2, right_suffix[2] + 2) = 1 + max(5 - 2, 1 + 2) = 1 + max(3, 3) = 1 + 3 = 4 So dp[1] = [2, 7, 4] (This matches my earlier calculation for dp[1])

Row 2 calculation: prev_row_dp = [2, 7, 4] left_prefix = [-inf] * M right_suffix = [-inf] * M

left_prefix[0] = prev_row_dp[0] + 0 = 2 for c = 1 to M-1: left_prefix[c] = max(left_prefix[c-1], prev_row_dp[c] + c) left_prefix = [2, max(2, 7+1=8)=8, max(8, 4+2=6)=8] left_prefix = [2, 8, 8]

right_suffix[M-1] = prev_row_dp[M-1] - (M-1) = 4 - 2 = 2 for c = M-2 down to 0: right_suffix[c] = max(right_suffix[c+1], prev_row_dp[c] - c) right_suffix = [max(6, 2-0=2)=6, max(2, 7-1=6)=6, 2] right_suffix = [6, 6, 2]

dp[2][0] = points[2][0] + max(left_prefix[0] - 0, right_suffix[0] + 0) = 3 + max(2 - 0, 6 + 0) = 3 + max(2, 6) = 3 + 6 = 9 dp[2][1] = points[2][1] + max(left_prefix[1] - 1, right_suffix[1] + 1) = 1 + max(8 - 1, 6 + 1) = 1 + max(7, 7) = 1 + 7 = 8 dp[2][2] = points[2][2] + max(left_prefix[2] - 2, right_suffix[2] + 2) = 1 + max(8 - 2, 2 + 2) = 1 + max(6, 4) = 1 + 6 = 7 So dp[2] = [9, 8, 7].

The maximum score is max(9, 8, 7) = 9. This matches.

Common mistakes candidates make

  • Naive O(N*M^2) DP: This is the most common mistake for this type of problem. The abs(c1 - c2) penalty is a strong hint for monotonic queue or prefix/suffix max optimization.
  • Off-by-one errors in indexing: Especially when dealing with prev_c relative to c.
  • Incorrectly applying prefix/suffix max: Ensure the terms being maximized (e.g., dp[r-1][c] + c and dp[r-1][c] - c) are correctly handled in the prefix/suffix passes.

Interview preparation tip

For the Array Matrix Dynamic Programming interview pattern, be on the lookout for O(N*M^2) DP solutions that can be optimized to O(N*M) using techniques like monotonic queue, prefix/suffix maximums, or difference arrays when an abs(i-j) penalty or similar structure appears in the recurrence. Practice recognizing and applying these optimizations.

Similar Questions