3225. Maximum Score From Grid Operations

  • Array
  • Dynamic Programming
  • Matrix
  • Prefix Sum
TIME
O(n^3)
SPACE
O(n^2)
problem on leetcode ↗source on github ↗

Intuition

For each column j, choose a "height" h[j] = number of black cells (0 to n). A white cell (i,j) scores if it's white (i >= h[j]) AND has adjacent black cell (i < h[j-1] OR i < h[j+1]).

Score from column j = sum of grid[i][j] for h[j] <= i < max(h[j-1], h[j+1]).

Solution

DP State

dp[a][b] = max score where h[prev_col] = a, h[curr_col] = b, with all previous columns fully scored.

Transition

When moving from column j to j+1:

  • Column j's contribution = psum[j][max(a, c)] - psum[j][b] if max(a, c) > b

Where a = h[j-1], b = h[j], c = h[j+1].

Optimization

Use prefix/suffix max arrays to reduce from O(n^4) to O(n^3):

  • suffix[a][b] = max over k >= a of (dp[k][b] + psum[j][k])
  • prefix[a][b] = max over k < a of dp[k][b]

For c > b: contribution comes from either a >= c or a < c case. For c <= b: contribution only from a > b.

Complexity

  • Time: O(n³)
  • Space: O(n²)

Edge Cases

  • Single column → score is 0
  • All zeros → score is 0
  • Large values requiring long long in Java

Code

from typing import List


class Solution:
    def maximumScore(self, grid: List[List[int]]) -> int:
        """
        3225. Maximum Score From Grid Operations
        Time: O(n^3)
        Space: O(n^2)
        """
        n = len(grid)

        # Precompute prefix sums for each column
        psum = [[0] * (n + 1) for _ in range(n)]
        for j in range(n):
            for i in range(n):
                psum[j][i + 1] = psum[j][i] + grid[i][j]

        NEG_INF = float("-inf")

        # dp[a][b] = max score, h[prev_col] = a, h[curr_col] = b
        dp = [[NEG_INF] * (n + 1) for _ in range(n + 1)]
        for b in range(n + 1):
            dp[0][b] = 0  # h[-1] = 0

        for j in range(n - 1):
            # suffix[a][b] = max over k >= a of (dp[k][b] + psum[j][k])
            suffix = [[NEG_INF] * (n + 1) for _ in range(n + 2)]
            # prefix[a][b] = max over k < a of dp[k][b]
            prefix = [[NEG_INF] * (n + 1) for _ in range(n + 2)]

            for b in range(n + 1):
                for a in range(n, -1, -1):
                    suffix[a][b] = max(dp[a][b] + psum[j][a], suffix[a + 1][b])

                for a in range(1, n + 2):
                    prev_dp = dp[a - 1][b] if a - 1 <= n else NEG_INF
                    prefix[a][b] = max(prefix[a - 1][b], prev_dp)

            new_dp = [[NEG_INF] * (n + 1) for _ in range(n + 1)]

            for b in range(n + 1):
                # Case: c <= b (contribution only from a > b)
                val = max(suffix[b + 1][b] - psum[j][b], prefix[b + 1][b])
                for c in range(b + 1):
                    new_dp[b][c] = val

                # Case: c > b
                for c in range(b + 1, n + 1):
                    val = max(suffix[c][b], prefix[c][b] + psum[j][c]) - psum[j][b]
                    new_dp[b][c] = val

            dp = new_dp

        # Final: add column n-1's contribution (h[n] = 0)
        ans = 0
        for a in range(n + 1):
            for b in range(n + 1):
                if dp[a][b] == NEG_INF:
                    continue
                contrib = psum[n - 1][a] - psum[n - 1][b] if a > b else 0
                ans = max(ans, dp[a][b] + contrib)

        return ans