Problem
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
forx >= 0
.-x
forx < 0
.
Examples
Example 1:
$$ points = \begin{bmatrix} 1 & 2 & \colorbox{lightblue} 3 \\ 1 & \colorbox{lightblue} 5 & 1 \\ \colorbox{lightblue} 3 & 1 & 1 \end{bmatrix} $$
Input: points =[[1,2,3],[1,5,1],[3,1,1]]
Output: 9
Explanation: The blue cells denote the optimal cells to pick, which have coordinates (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:
$$ grid = \begin{bmatrix} 1 & \colorbox{lightblue} 5 \\ 2 & \colorbox{lightblue} 3 \\ \colorbox{lightblue} 4 & 2 \end{bmatrix} $$
Input: points =[[1,5],[2,3],[4,2]]
Output: 11
Explanation: The blue cells denote the optimal cells to pick, which have coordinates (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.
Solution
Method 1 - Simple Dynamic Programming
Code
Java
class Solution {
public long maxPoints(int[][] points) {
int m = points.length;
int n = points[0].length;
long[][] dp = new long[m][n]; // null => not set
// Set the first row of dp to the first row of points
for (int i = 0; i < n; ++i) {
dp[0][i] = points[0][i];
}
// Process the dp table
for (int i = 1; i < m; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
dp[i][j] = Math.max(dp[i][j],
dp[i - 1][k] - Math.abs(k - j) + points[i][j]);
}
}
}
// Find the maximum value in the last row of dp
long maxAns = -1;
for (long v : dp[m - 1]) {
maxAns = Math.max(maxAns, v);
}
return maxAns;
}
}
Complexity
- ⏰ Time complexity:
O(m*n^2)
wherem
is number of rows, andn
is number of columns- This will result in TLE.
- 🧺 Space complexity:
O(m*n)
Method 2 - Optimal DP with left and right max
Previous solution was good, but we don’t want to literally compare every index in previous row to find the max points in the current row.
But if we look closely, the current row’s points for a certain index j, can also come from its left or right (inclusive). So, we can build two arrays - leftMax
and rightMax
, and focus on the maximum value coming from its left or right.
So, we can take leftMax[0] = prev[0]
, where prev is previous row.
Now, leftMax[1] = max(prev[1], leftMax[0] - 1)
Similarly, leftMax[2] = max(prev[2], leftMax[1] - 1)
Now, question is why it is sufficient to compareleftMax[1] - 1
and not leftMax[0] - 2
for filling in leftMax[2]
? And the reason is we already chose the optimal answer in leftMax[1]
.
Similarly, we build rightMax
from the right.
rightMax[n - 1] = prev[n - 1]
rightMax[n - 2] = max(prev[n - 2], rightMax[n - 1] - 1)
.
Approach
- Initialization: Use a DP array to keep track of the maximum points that can be achieved for each cell in the current row.
- Transition: For each cell in the current row, we need to consider all cells from the previous row. The points gained from the current cell should include the points from the previous cell minus the distance penalty.
- Optimization: To optimize the calculation, we can maintain two forward and backward sweeps to precompute the best points that can be achieved from any cell.
Code
Java
public class Solution {
public long maxPoints(int[][] points) {
int m = points.length;
int n = points[0].length;
long[] dp = new long[n];
// Initially copy the first row into dp
for (int j = 0; j < n; j++) {
dp[j] = points[0][j];
}
// Process each row starting from the second row
for (int i = 1; i < m; i++) {
long[] leftMax = new long[n];
long[] rightMax = new long[n];
// Calculate the maximum values considering left to right
leftMax[0] = dp[0];
for (int j = 1; j < n; j++) {
leftMax[j] = Math.max(leftMax[j - 1] - 1, dp[j]);
}
// Calculate the maximum values considering right to left
rightMax[n - 1] = dp[n - 1];
for (int j = n - 2; j >= 0; j--) {
rightMax[j] = Math.max(rightMax[j + 1] - 1, dp[j]);
}
// Update dp array with the maximum achievable points for the
// current row
for (int j = 0; j < n; j++) {
dp[j] = points[i][j] + Math.max(leftMax[j], rightMax[j]);
}
}
// The result is the maximum value in the dp array
long maxPoints = 0;
for (int j = 0; j < n; j++) {
maxPoints = Math.max(maxPoints, dp[j]);
}
return maxPoints;
}
}
Complexity
- Time:
O(m*n)
- Space:
O(m)