Welcome to Subscribe On Youtube
3888. Minimum Operations to Make All Grid Elements Equal 🔒
Description
You are given a 2D integer array grid of size m × n, and an integer k.
In one operation, you can:
- Select any
k x ksubmatrix ofgrid, and - Increment all elements inside that submatrix by 1.
Return the minimum number of operations required to make all elements in the grid equal. If it is not possible, return -1.
A submatrix (x1, y1, x2, y2) is a matrix that forms by choosing all cells matrix[x][y] where x1 <= x <= x2 and y1 <= y <= y2.
Example 1:
Input: grid = [[3,3,5],[3,3,5]], k = 2
Output: 2
Explanation:
Choose the left 2 x 2 submatrix (covering the first two columns) and apply the operation twice.
- After 1 operation:
[[4, 4, 5], [4, 4, 5]] - After 2 operations:
[[5, 5, 5], [5, 5, 5]]
All elements become equal to 5. Thus, the minimum number of operations is 2.
Example 2:
Input: grid = [[1,2],[2,3]], k = 1
Output: 4
Explanation:
Since k = 1, each operation increments a single cell grid[i][j] by 1. To make all elements equal, the final value must be 3.
- Increase
grid[0][0] = 1to 3, requiring 2 operations. - Increase
grid[0][1] = 2to 3, requiring 1 operation. - Increase
grid[1][0] = 2to 3, requiring 1 operation.
Thus, the minimum number of operations is 2 + 1 + 1 + 0 = 4.
Constraints:
1 <= m == grid.length <= 10001 <= n == grid[i].length <= 1000-105 <= grid[i][j] <= 1051 <= k <= min(m, n)
Solutions
Solution 1: 2D Difference Array + Greedy
Since the operation can only increase the value of elements, all elements in the final grid must be equal to some target value $T$, and $T \ge \max(\textit{grid})$.
Start traversing the grid from the top-left corner $(0, 0)$. For any position $(i, j)$, if its current value is less than $T$, since subsequent operations (with a more rightward or downward position as the top-left corner) cannot cover $(i, j)$, it is necessary to perform $T - \text{current_val}$ operations at the current position, each using $(i, j)$ as the top-left corner of a $k \times k$ increment operation.
If each operation traverses the $k \times k$ region, the complexity will reach $O(m \cdot n \cdot k^2)$. We can use a 2D difference array $\textit{diff}$ to record the operations. By maintaining the 2D prefix sum of $\textit{diff}$ in real time, we can obtain the cumulative increment at the current position in $O(1)$ time, and update the future impact of a $k \times k$ region in $O(1)$ time.
In most cases, $T = \max(\textit{grid})$ is sufficient. However, in some cases where $k \times k$ regions overlap, a smaller $T$ may cause the middle positions to be passively increased beyond $T$. According to mathematical consistency, if both $T = \max(\textit{grid})$ and $T = \max(\textit{grid}) + 1$ are not feasible, then it is impossible to flatten the grid using $k \times k$ operations.
The time complexity is $O(m \times n)$ and the space complexity is $O(m \times n)$, where $m$ and $n$ are the number of rows and columns of the grid, respectively.
-
class Solution { int[][] grid; int m, n, k; public long minOperations(int[][] grid, int k) { this.grid = grid; this.k = k; this.m = grid.length; this.n = grid[0].length; int mx = Integer.MIN_VALUE; for (int[] row : grid) { for (int v : row) { mx = Math.max(mx, v); } } for (int t = mx; t <= mx + 1; t++) { long res = check(t); if (res != -1) { return res; } } return -1; } private long check(int target) { long[][] diff = new long[m + 2][n + 2]; long totalOps = 0; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1]; long cur = grid[i - 1][j - 1] + diff[i][j]; if (cur > target) { return -1; } if (cur < target) { if (i + k - 1 > m || j + k - 1 > n) { return -1; } long need = target - cur; totalOps += need; diff[i][j] += need; diff[i + k][j] -= need; diff[i][j + k] -= need; diff[i + k][j + k] += need; } } } return totalOps; } } -
class Solution { public: long long minOperations(vector<vector<int>>& grid, int k) { int m = grid.size(); int n = grid[0].size(); int mx = grid[0][0]; for (auto& row : grid) { for (int val : row) { mx = max(mx, val); } } auto check = [&](int target) -> long long { vector<vector<long long>> diff(m + 2, vector<long long>(n + 2, 0)); long long total_ops = 0; for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1]; long long cur_val = grid[i - 1][j - 1] + diff[i][j]; if (cur_val > target) { return -1; } if (cur_val < target) { if (i + k - 1 > m || j + k - 1 > n) { return -1; } long long needed = target - cur_val; total_ops += needed; diff[i][j] += needed; diff[i + k][j] -= needed; diff[i][j + k] -= needed; diff[i + k][j + k] += needed; } } } return total_ops; }; for (int t = mx; t <= mx + 1; ++t) { long long res = check(t); if (res != -1) { return res; } } return -1; } }; -
class Solution: def minOperations(self, grid: list[list[int]], k: int) -> int: m, n = len(grid), len(grid[0]) mx = max(max(row) for row in grid) def check(target: int) -> int: diff = [[0] * (n + 2) for _ in range(m + 2)] total_ops = 0 for i, row in enumerate(grid, 1): for j, val in enumerate(row, 1): diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1] cur_val = val + diff[i][j] if cur_val > target: return -1 if cur_val < target: if i + k - 1 > m or j + k - 1 > n: return -1 needed = target - cur_val total_ops += needed diff[i][j] += needed diff[i + k][j] -= needed diff[i][j + k] -= needed diff[i + k][j + k] += needed return total_ops for t in range(mx, mx + 2): res = check(t) if res != -1: return res return -1 -
func minOperations(grid [][]int, k int) int64 { m, n := len(grid), len(grid[0]) maxVal := grid[0][0] for _, row := range grid { maxVal = max(maxVal, slices.Max(row)) } check := func(target int) int64 { diff := make([][]int64, m+2) for i := range diff { diff[i] = make([]int64, n+2) } var totalOps int64 for i := 1; i <= m; i++ { for j := 1; j <= n; j++ { diff[i][j] += diff[i-1][j] + diff[i][j-1] - diff[i-1][j-1] curVal := int64(grid[i-1][j-1]) + diff[i][j] if curVal > int64(target) { return -1 } if curVal < int64(target) { if i+k-1 > m || j+k-1 > n { return -1 } needed := int64(target) - curVal totalOps += needed diff[i][j] += needed diff[i+k][j] -= needed diff[i][j+k] -= needed diff[i+k][j+k] += needed } } } return totalOps } for t := maxVal; t <= maxVal+1; t++ { if res := check(t); res != -1 { return res } } return -1 } -
function minOperations(grid: number[][], k: number): number { const m = grid.length; const n = grid[0].length; let maxVal = grid[0][0]; for (const row of grid) { for (const val of row) { maxVal = Math.max(maxVal, val); } } const check = (target: number): number => { const diff: number[][] = Array.from({ length: m + 2 }, () => Array(n + 2).fill(0)); let totalOps = 0; for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1]; const curVal = grid[i - 1][j - 1] + diff[i][j]; if (curVal > target) return -1; if (curVal < target) { if (i + k - 1 > m || j + k - 1 > n) return -1; const needed = target - curVal; totalOps += needed; diff[i][j] += needed; diff[i + k][j] -= needed; diff[i][j + k] -= needed; diff[i + k][j + k] += needed; } } } return totalOps; }; for (let t = maxVal; t <= maxVal + 1; t++) { const res = check(t); if (res !== -1) return res; } return -1; }