Skip to content

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 k submatrix of grid, 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] = 1 to 3, requiring 2 operations.
  • Increase grid[0][1] = 2 to 3, requiring 1 operation.
  • Increase grid[1][0] = 2 to 3, requiring 1 operation.

Thus, the minimum number of operations is 2 + 1 + 1 + 0 = 4.

Β 

Constraints:

  • 1 <= m == grid.length <= 1000
  • 1 <= n == grid[i].length <= 1000
  • -105 <= grid[i][j] <= 105
  • 1 <= 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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;
}

Comments