1937. Maximum Number of Points with Cost

Latest:

https://www.youtube.com/watch?v=ik1y7fz8AOc

class Solution {
    public long maxPoints(int[][] points) {
        int m = points.length;
        int n = points[0].length;
        long[] prevRow = new long[n];
        long[] curRow = new long[n];
        
        for (int j = 0; j < n; j++) { // for big number add...use long
            prevRow[j] = points[0][j];
        }
        long result = 0;

        for (int i = 1; i < m; i++) {
            long[] leftMax = new long[n];
            long[] rightMax = new long[n];

            leftMax[0] = prevRow[0];
            for (int j = 1; j < n; j++) {
                leftMax[j] = Math.max(leftMax[j-1] - 1, prevRow[j]);
            }

            rightMax[n-1] = prevRow[n-1];
            for (int j = n-2; j >= 0; j--) {
                rightMax[j] = Math.max(rightMax[j+1] - 1, prevRow[j]);
            }
            for (int j = 0; j < n; j++) {
                curRow[j] = points[i][j];
                curRow[j] += Math.max(leftMax[j], rightMax[j]);
            }
            prevRow = curRow;
        }

        for (int j = 0; j < n; j++) {
            result = Math.max(result, prevRow[j]); // for only one row case, if it's one row, then ans is in prevRow
        }
        return result;
    }
}
/**

cal left max array
   right max  
if m = 3, left start from 1 , right start from m-2
 left max = left[1] = max(left[1-1] - 1, prev_row[1])  
 right max = right[1] = max(right[3-2] - 1, prev_row[1]) 
so we can have left max and right max
curr_row[1] = curr_row[1] + max(left_max[1], right_max[1])
continuously cal to last row, then ans is the max(last_row)

          1 2 3
left max for 5          ->.  5 + max(1a - 1, 2) = 7
          1a 5 1c
right max for 5.        ->  5 + max(1c -1, 2) =  7

      1. 2 3
left. 1. 2 3
right 1  2 3
      1  5.1 ->  1 + max(1, 1), 5 + max(2, 2), 1 + max(3, 3) = 
      2  7 4 

so 1 5 1 to
          2 7 4
left      2 7 6          
right     6 7 4 
          3 1 1.   3 + max(left[0], right[0]) = 3 + max(2, 6) = 9

          9 8 7.  ->.  max(last row) = 9

so this way is 
T: O(mn), m is row, n is col, for each row, cal left_max (n), and right_max(n) -> m*(2n)
S: O(n), left_max, right_max array          
 */

like 931, 但分支太多, 所以 dfs + memo 會不過 (931 只有 3個分支, 這題有 n 個分支(n is num of col)

DFS + memo (Time Limit Exceeded

T: O(col^row)

S: O(row*col)

class Solution {
    public long maxPoints(int[][] points) {
        int m = points.length;
        int n = points[0].length;
        
        Long[][] memo = new Long[m][n];
        long max = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) { // each cell in row 0
            max = Math.max(max, dfs(points, 0, i, memo));
        }
        return max;
    }
    private long dfs(int[][] points, int row, int col, Long[][] memo) {
        int m = points.length;
        int n = points[0].length;
        
        if(row == m - 1) {
            return points[row][col];
        }
        
        if (memo[row][col] != null) {
            return memo[row][col];
        }
        
        long max = Long.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            // notice, in dfs, run (row+1, all cols)            
            long sum = points[row][col] + dfs(points, row+1, i, memo) - Math.abs(col - i);
            max = Math.max(max, sum);
        }
        memo[row][col] = max;
        return max;
    }

}

/*
row 0, col 0~3

p(0,0)
+
(r+1, col i 0~3) - abs|i - col|


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


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

*/

DP

class Solution {
    public long maxPoints(int[][] points) {
        int m = points.length;
        int n = points[0].length;
        long[][] dp = new long[m][n];
        
        
        for (int j = 0; j < n; j++) { 
            dp[0][j] = points[0][j]; // first row value
        }
        
        for (int i = 1; i < m; i++) {
            long rollingMax = Long.MIN_VALUE; // 每次 row 都會拿到一個最大值, 所以要宣告在這
            for (int j = 0; j < n; j++) {
                // System.out.println("i:"+i);
                // System.out.println("dp[i-1][j]:"+dp[i-1][j]+",j:"+ j);
                // System.out.println("dp[i-1][j] + j:"+ (dp[i-1][j] + j)+"\n");
                
                rollingMax = Math.max(rollingMax, dp[i-1][j] + j);
                // System.out.println("rollingMax:" + rollingMax);
                dp[i][j] = Math.max(dp[i][j], (points[i][j] - j) +  rollingMax);

            }
            rollingMax = Long.MIN_VALUE;
            for (int j = n-1; j >= 0; j--) { // why reverse?
                // System.out.println("i:"+i);
                // System.out.println("dp[i-1][j]:"+dp[i-1][j]+",j:"+ j);
                // System.out.println("dp[i-1][j] - j:"+ (dp[i-1][j] - j)+"\n");
                rollingMax = Math.max(rollingMax, dp[i-1][j] - j);
                // System.out.println("rollingMax:" + rollingMax);
                dp[i][j] = Math.max(dp[i][j], (points[i][j] + j) +  rollingMax);
            }
        }
        /*[
        [1,5],
        [2,3],
        [4,2]]
        */

        long res = Long.MIN_VALUE;
        for (int j = 0; j < n; j++) {
            res = Math.max(res, dp[m-1][j]);
        }
        return res;
    }
}

/*
sol 1.
for (int i = 0 ~m)
    for (int j = 0~n)
      for(int k = 0 ~n)
            dp[i][j] = Max(points[i][j] + dp[i-1][k] + abs(j-k))

T: O(m*n*n), => TLE, m, n is 10^5, too big


sol 2.
T: O(m*n)

dp[i][j] = from row 0~m-1, points[i][j] which is the maximum points you can obtain

dp[i][j] = Max(points[i][j] + dp[i-1][k] + abs(j-k)) => 

when j-k >= 0
dp[i][j] = (points[i][j] + j) + max(dp[i-1][k] -k), k= 0, 1, ...~j (從0 開始滾)

dp[i][0] = (points[i][0] + 0) + max(dp[i-1][0] -0)
dp[i][1] = (points[i][1] + 1) + max(dp[i-1][0] -0, dp[i-1][1] -1)
dp[i][2] = (points[i][2] + 2) + max(dp[i-1][0] -0, dp[i-1][1] -1, dp[i-1][2] -2 )...

=>

for (int i = 1 ~m)
    for (int j = 0~n)
        int rollingMax = Math.max(rollingMax, dp[i-1][j] +j);
        dp[i][j] = Math.max(dp[i][j], (points[i][j] - j) +  rollingMax);

when j-k <= 0, 就反過來
dp[i][j] = (points[i][j] - j) + max(dp[i-1][k] + k), k= j, j+1, ...~n-1 (從 n-1 開始滾)

為什麼. for k>=j 時
for (int j=n-1; j>=0; j--)
{
    rollingMax = max(rollingMax, dp[i-1][j]-j);
    dp[i][j] = max(dp[i][j], rollingMax +points[i][j] + j);
}
一定要從 n-1 後面 rolling 過來呢

因為這裡是 -k, 所以從後面大的開始滾, max 的值會從小到大滾出來



for (int i = 1 ~m)
    for (int j = 0~n)=> 這不行, 要到序
        int rollingMax = Math.max(rollingMax, dp[i-1][j] - j);
        dp[i][j] = Math.max(dp[i][j], (points[i][j] + j) +  rollingMax);
*/

another explain

/*
for i
 for j
    for (k = 0~n) {
    dp[i][j] = max(dp[i-1][k] - abs(j - k)) + A[i][j]
 }
 
how to reduce for (k = 0~n), this part?

abs(j-k) => can be two cases

 1. j - k >= 0 => - abs(j - k) => -(j - k) = k - j, k = 0~j
 
 dp[i][j] = max(dp[i-1][k] + abs(j - k)) + A[i][j]
 
    rolling part
 = max(dp[i-1][k] + k) - j + A[i][j]
 
 2. j - k <= 0 => - j + k
 
    rolling part
 = max(dp[i-1][k] - k) + j + A[i][j]
 
 
 rolling max:
 
 max(dp[i-1][k] + k) , k = 0 ~ j
 for small to max : start from 0 to n-1 rolling
 
 
 max(dp[i-1][k] - k) , k = j~n-1
 for small to max : start from n-1 to 0 rolling
 
 T: O(mn)
 S: O(mn)
 
*/

/*
dp[i][j] = Max(dp[i-1][k] - abs(j-k) + point[i][j])

=> 
 for (int i)
  for (int j)
    for (int k)

how to optimized?

j - k >= 0, k = 0~j

dp[i][j] = max(point[i][j] + dp[i-1][k] - j + k)

=>
dp[i][j] = max(dp[i-1][k] + k) + point[i][j] - j

k = 0~j
dp[i][0] = max(dp[i-1][0] + 0) + point[i][0] - 0

dp[i][1] = max(dp[i-1][0] + 0, dp[i-1][1] + 1) + point[i][1] - 1

dp[i][2] = max(dp[i-1][0] + 0, dp[i-1][1] + 1, dp[i-1][2] + 2) + point[i][2] - 2

for (int i)
  for (int j)
    long rollingMax = max(rollingMax, dp[i-1][j] + j)
    dp[i][j] = max(dp[i][j], rollingMax + point[i][j] - j)

----------------------
j - k <= 0, k = j ~ n

dp[i][j] = dp[i-1][k] + j - k

=>
dp[i][j] = dp[i-1][k] - k + j


for (int i)
  for (int j)
    long rollingMax = max(rollingMax, dp[i-1][j] - j)
    dp[i][j] = max(dp[i][j], rollingMax + point[i][j] + j)
    

at last find max in dp[m-1][0~n]
*/

Last updated