跳转至

3888. Minimum Operations to Make All Grid Elements Equal 🔒

题目描述

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)

解法

方法一:二维差分 + 贪心

由于操作只能增加元素的值,因此最终网格的所有元素必须等于某个目标值 \(T\),且 \(T \ge \max(\textit{grid})\)

从左上角 \((0, 0)\) 开始遍历网格。对于任意位置 \((i, j)\),如果它当前的数值小于 \(T\),由于后续的操作(以更靠右或更靠下的位置为左上角的操作)都无法覆盖到 \((i, j)\),因此必须在当前位置执行 \(T - \text{current\_val}\) 次以 \((i, j)\) 为左上角的 \(k \times k\) 增加操作。

如果每次执行操作都遍历 \(k \times k\) 区域,复杂度将达到 \(O(m \cdot n \cdot k^2)\)。我们可以使用二维差分数组 \(\textit{diff}\) 来记录操作。通过实时维护 \(\textit{diff}\) 的二维前缀和,我们可以在 \(O(1)\) 时间内获取当前位置的累计增量,并在 \(O(1)\) 时间内更新一个 \(k \times k\) 区域的未来影响。

通常情况下 \(T = \max(\textit{grid})\) 即可。但在某些 \(k \times k\) 覆盖重叠的情况下,较小的 \(T\) 可能导致中间位置被动增加后超过 \(T\)。根据数学一致性,若 \(T = \max(\textit{grid})\)\(T = \max(\textit{grid}) + 1\) 均不可行,则该网格无法通过 \(k \times k\) 操作变平。

时间复杂度 \(O(m \times n)\),空间复杂度 \(O(m \times n)\),其中 \(m\)\(n\) 分别是网格的行数和列数。

 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;
}

评论