You are given an m x n integer matrix points (0-indexed). Starting with 0 points, you want to maximize the number of points you can get from the matrix.
To gain points, you must pick one cell in each row. Picking the cell at coordinates (r, c) will add points[r][c] to your score.
However, you will lose points if you pick a cell too far from the cell that you picked in the previous row. For every two adjacent rows r and r + 1 (where 0 <= r < m - 1), picking cells at coordinates (r, c1) and (r + 1, c2) will subtract abs(c1 - c2) from your score.
Return the maximum number of points you can achieve.
abs(x) is defined as:
x for x >= 0.-x for x < 0.Example 1:
Input: points = [
[1,2,3],
[1,5,1],
[3,1,1]
]
Output: 9Explanation: The optimal cells to pick are (0, 2), (1, 1), and (2, 0).
You add 3 + 5 + 3 = 11 to your score.
However, you must subtract abs(2 - 1) + abs(1 - 0) = 2 from your score.
Your final score is 11 - 2 = 9.
Example 2:
Input: points = [
[1,5],
[2,3],
[4,2]
]
Output: 11Explanation: The optimal cells to pick are (0, 1), (1, 1), and (2, 0).
You add 5 + 3 + 4 = 12 to your score.
However, you must subtract abs(1 - 1) + abs(1 - 0) = 1 from your score.
Your final score is 12 - 1 = 11.
Constraints:
m == points.lengthn == points[r].length1 <= m, n <= 100,0001 <= m * n <= 100,0000 <= points[r][c] <= 100,000Before attempting this problem, you should be comfortable with:
We need to pick one cell from each row such that the total points are maximized. The tricky part is the penalty: if we pick column c in row r and column c' in row r+1, we lose |c - c'| points.
A natural approach is to try all possibilities. For each starting column in the first row, we recursively explore all column choices in subsequent rows, subtracting the movement penalty and adding the cell value. We return the maximum sum found.
dfs(r, c) that returns the maximum points obtainable from row r+1 to the last row, given we are currently at column c in row r.r is the last row, return 0 (no more rows to process).col in the next row, compute points[r+1][col] - |col - c| + dfs(r+1, col).points[0][c] + dfs(0, c) for all starting columns c.class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
m, n = len(points), len(points[0])
def dfs(r, c):
if r == m - 1:
return 0
res = 0
for col in range(n):
res = max(res, points[r + 1][col] - abs(col - c) + dfs(r + 1, col))
return res
ans = 0
for c in range(n):
ans = max(ans, points[0][c] + dfs(0, c))
return ansWhere is the number of rows, and is the number of columns.
The recursive solution recalculates the same subproblems multiple times. For a given (row, column) pair, the maximum points from that position onward is always the same, regardless of how we got there.
By storing (memoizing) the result for each (r, c) pair, we avoid redundant computation. This transforms the exponential solution into a polynomial one.
memo to cache results for each (r, c) pair.dfs(r, c) as before, but first check if the result is already cached.class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
m, n = len(points), len(points[0])
memo = {}
def dfs(r, c):
if (r, c) in memo:
return memo[(r, c)]
if r == m - 1:
return 0
res = 0
for col in range(n):
res = max(res, points[r + 1][col] - abs(col - c) + dfs(r + 1, col))
memo[(r, c)] = res
return res
ans = 0
for c in range(n):
ans = max(ans, points[0][c] + dfs(0, c))
return ansWhere is the number of rows, and is the number of columns.
Computing max(dp[c'] - |c - c'|) for each column c normally takes O(n) time per cell, making the total O(m * n^2). We can optimize this using prefix maximums.
The penalty |c - c'| splits into two cases:
c' <= c: the penalty is c - c', so we want max(dp[c'] + c') for all c' <= c.c' > c: the penalty is c' - c, so we want max(dp[c'] - c') for all c' > c.By precomputing a left array (max of dp[c'] + c' from the left) and a right array (max of dp[c'] - c' from the right), we can answer each column's query in O(1) time.
dp with the first row's values.left[c] = max over all c' <= c of dp[c']. Propagate left-to-right, subtracting 1 at each step.right[c] = max over all c' >= c of dp[c']. Propagate right-to-left, subtracting 1 at each step.c, set nextDp[c] = points[r][c] + max(left[c], right[c]).dp.class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
ROWS, COLS = len(points), len(points[0])
dp = points[0]
for r in range(1, ROWS):
left = [0] * COLS
left[0] = dp[0]
for c in range(1, COLS):
left[c] = max(dp[c], left[c - 1] - 1)
right = [0] * COLS
right[COLS - 1] = dp[COLS - 1]
for c in range(COLS - 2, -1, -1):
right[c] = max(dp[c], right[c + 1] - 1)
nextDp = points[r][:]
for c in range(COLS):
nextDp[c] += max(left[c], right[c])
dp = nextDp
return max(dp)Where is the number of rows, and is the number of columns.
We can reduce memory usage by combining the left and right sweeps into a single pass. Instead of storing separate left and right arrays, we build the left maximum in a cur array during the left-to-right pass. Then, during the right-to-left pass, we update cur[c] by taking the max of the current left value and the running right maximum, adding the cell value as we go.
This avoids allocating a separate right array, reducing the constant factor in space usage.
prev with the first row's values.cur array and build left maximums by iterating left-to-right: cur[c] = max(prev[c], cur[c-1] - 1).rightMax. For each column, update cur[c] = max(cur[c], rightMax) + points[r][c], then update rightMax = max(prev[c], rightMax - 1).prev = cur for the next iteration.prev.class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
ROWS, COLS = len(points), len(points[0])
prev = points[0]
for r in range(1, ROWS):
cur = [0] * COLS
cur[0] = prev[0]
for c in range(1, COLS):
cur[c] = max(prev[c], cur[c - 1] - 1)
rightMax = prev[COLS - 1]
for c in range(COLS - 2, -1 , -1):
rightMax = max(prev[c], rightMax - 1)
cur[c] = max(cur[c], rightMax) + points[r][c]
cur[COLS - 1] += points[r][COLS - 1]
prev = cur
return max(prev)Where is the number of rows, and is the number of columns.
With grid dimensions up to 10^5 and cell values up to 10^5, the maximum total points can exceed the 32-bit integer limit. Using int instead of long (or equivalent 64-bit type) will cause overflow and incorrect results.
The optimized solution requires computing both left-to-right and right-to-left prefix maximums. Forgetting one direction or incorrectly combining them will miss optimal transitions from certain columns, leading to suboptimal answers.
When propagating the prefix maximum, the penalty decreases by 1 for each column of distance. A common error is applying the penalty incorrectly, such as subtracting the penalty before comparing or not decrementing for each step. The update should be left[c] = max(dp[c], left[c-1] - 1), not left[c] = max(dp[c] - 1, left[c-1]).