You are given a 2D array points
with m
rows and n
columns, where points[i][j]
represents the number of points you can earn at cell (i, j)
.
You must pick one cell from each row, starting from the first row to the last. The total score you earn is the sum of the values of all chosen cells, but with a penalty: if you pick cell (i, j)
and then cell (i+1, k)
in the next row, you pay a cost of abs(j - k)
(the absolute difference of their column indices).
Your goal is to maximize your total score by choosing one cell from each row, accounting for the penalty at each step.
Constraints:
Input: points = [[1,2,3],[1,5,1],[3,1,1]] Output: 9 Explanation: Pick cells (0,2) -> (1,1) -> (2,0): 3 + 5 + 3 = 11, penalties are |2-1|=1 and |1-0|=1, so total = 11 - 2 = 9.
The first instinct might be to try all possible paths: for each row, try every possible cell and keep track of the total score and penalty. However, this brute-force approach quickly becomes infeasible due to the exponential number of possible paths as the matrix grows.
Recognizing that each choice in a row only depends on the choices made in the previous row, we can think in terms of dynamic programming. At each step, we want to know, for each cell in the current row, the best score we can achieve if we end up at that cell, considering all possible ways to get there from the previous row.
The main challenge is efficiently handling the penalty, which depends on the distance between columns in consecutive rows. Naively, for each cell, we could check every cell in the previous row, but this leads to an O(m*n*n) solution, which can be too slow. The key is to optimize this transition using clever observations about the penalty structure.
We use dynamic programming to solve this problem efficiently. Here's a step-by-step breakdown:
dp[i][j]
be the maximum score we can get by picking cell (i, j)
on row i
.
dp[0][j] = points[0][j]
for all columns j
.
(i, j)
in row i
, we want to compute:
dp[i][j] = max(dp[i-1][k] - abs(j - k)) + points[i][j]
for all k
in previous row.
j
, keep track of the maximum value of dp[i-1][k] + k
seen so far, and subtract j
(since -abs(j-k) = k-j
when k <= j
).
j
, keep track of the maximum value of dp[i-1][k] - k
seen so far, and add j
(since -abs(j-k) = j-k
when k >= j
).
dp
.
Let's use the sample input: points = [[1,2,3],[1,5,1],[3,1,1]]
Step 1: Initialize DP for row 0
Brute-force Approach:
The problem asks us to maximize the total points earned by picking one cell per row in a grid, paying a penalty for switching columns between rows. By recognizing the structure of the penalties, we can use dynamic programming and optimize the state transitions with two linear passes per row. This reduces what could be an exponential or quadratic problem to a linear one in terms of the number of cells, making it both elegant and efficient.
class Solution:
def maxPoints(self, points):
m, n = len(points), len(points[0])
dp = points[0][:]
for i in range(1, m):
left = [0] * n
right = [0] * n
left[0] = dp[0]
for j in range(1, n):
left[j] = max(left[j-1]-1, dp[j])
right[-1] = dp[-1]
for j in range(n-2, -1, -1):
right[j] = max(right[j+1]-1, dp[j])
for j in range(n):
dp[j] = points[i][j] + max(left[j], right[j])
return max(dp)
class Solution {
public:
long long maxPoints(vector<vector<int>>& points) {
int m = points.size(), n = points[0].size();
vector<long long> dp(points[0].begin(), points[0].end());
for (int i = 1; i < m; ++i) {
vector<long long> left(n), right(n);
left[0] = dp[0];
for (int j = 1; j < n; ++j)
left[j] = max(left[j-1] - 1, dp[j]);
right[n-1] = dp[n-1];
for (int j = n-2; j >= 0; --j)
right[j] = max(right[j+1] - 1, dp[j]);
for (int j = 0; j < n; ++j)
dp[j] = points[i][j] + max(left[j], right[j]);
}
return *max_element(dp.begin(), dp.end());
}
};
class Solution {
public long maxPoints(int[][] points) {
int m = points.length, n = points[0].length;
long[] dp = new long[n];
for (int j = 0; j < n; j++) dp[j] = points[0][j];
for (int i = 1; i < m; i++) {
long[] left = new long[n], right = new long[n];
left[0] = dp[0];
for (int j = 1; j < n; j++)
left[j] = Math.max(left[j-1] - 1, dp[j]);
right[n-1] = dp[n-1];
for (int j = n-2; j >= 0; j--)
right[j] = Math.max(right[j+1] - 1, dp[j]);
for (int j = 0; j < n; j++)
dp[j] = points[i][j] + Math.max(left[j], right[j]);
}
long ans = 0;
for (long v : dp) ans = Math.max(ans, v);
return ans;
}
}
var maxPoints = function(points) {
const m = points.length, n = points[0].length;
let dp = points[0].slice();
for (let i = 1; i < m; i++) {
let left = Array(n), right = Array(n);
left[0] = dp[0];
for (let j = 1; j < n; j++)
left[j] = Math.max(left[j-1] - 1, dp[j]);
right[n-1] = dp[n-1];
for (let j = n-2; j >= 0; j--)
right[j] = Math.max(right[j+1] - 1, dp[j]);
for (let j = 0; j < n; j++)
dp[j] = points[i][j] + Math.max(left[j], right[j]);
}
return Math.max(...dp);
};