Minimum Number of Operations to Satisfy Conditions
MedPrompt
You are given a 2D matrix grid of size m x n. In one operation, you can change the value of any cell to any non-negative number. You need to perform some operations such that each cell grid[i][j] is:
- Equal to the cell below it, i.e.
grid[i][j] == grid[i + 1][j](if it exists). - Different from the cell to its right, i.e.
grid[i][j] != grid[i][j + 1](if it exists).
Return the minimum number of operations needed.
Example 1:
Input: grid = [[1,0,2],[1,0,2]]
Output: 0
Explanation:

All the cells in the matrix already satisfy the properties.
Example 2:
Input: grid = [[1,1,1],[0,0,0]]
Output: 3
Explanation:

The matrix becomes [[1,0,1],[1,0,1]] which satisfies the properties, by doing these 3 operations:
- Change
grid[1][0]to 1. - Change
grid[0][1]to 0. - Change
grid[1][2]to 1.
Example 3:
Input: grid = [[1],[2],[3]]
Output: 2
Explanation:

There is a single column. We can change the value to 1 in each cell using 2 operations.
Constraints:
1 <= n, m <= 10000 <= grid[i][j] <= 9
Approaches
3 approaches with complexity analysis and trade-offs.
The problem can be framed as finding an optimal sequence of values v_0, v_1, ..., v_{n-1} for each column, where v_j is the value assigned to all cells in column j. The constraints are that v_j must be different from v_{j+1}. The cost for assigning v_j to column j is m - (number of cells in column j already equal to v_j).
A brute-force approach explores every possible valid sequence of values for the columns. We can define a recursive function, say findMinOps(colIndex, prevVal), which calculates the minimum operations needed for columns from colIndex to n-1, given that the previous column (colIndex - 1) was assigned the value prevVal.
In this function, we iterate through all possible values (0-9) for the current column colIndex. If a value currVal is different from prevVal, we calculate the cost for changing column colIndex to currVal and recursively call the function for the next column: findMinOps(colIndex + 1, currVal). The total cost for this choice is the sum of the current column's cost and the result of the recursive call. We take the minimum over all valid choices for currVal.
Algorithm
- Pre-calculate the frequency of each number (0-9) for each column. Store this in a
counts[n][10]array. This takesO(m*n). - Define a recursive function
solve(col, prev_val):- Base Case: If
col == n(all columns processed), return 0. - Initialize
min_ops = infinity. - Iterate
valfrom 0 to 9:- If
val != prev_val:- Calculate cost for the current column
colto beval:cost = m - counts[col][val]. - Recursively find the cost for the rest of the columns:
future_cost = solve(col + 1, val). - Update
min_ops = min(min_ops, cost + future_cost).
- Calculate cost for the current column
- If
- Return
min_ops.
- Base Case: If
- The initial call is
solve(0, -1)(using -1 to signify no previous column).
Walkthrough
class Solution { private int m, n; private int[][] counts; public int minimumOperations(int[][] grid) { this.m = grid.length; this.n = grid[0].length; this.counts = new int[n][10]; for (int j = 0; j < n; j++) { for (int i = 0; i < m; i++) { counts[j][grid[i][j]]++; } } return solve(0, -1); } private int solve(int col, int prevVal) { if (col == n) { return 0; } int minOps = Integer.MAX_VALUE; for (int val = 0; val <= 9; val++) { if (val != prevVal) { int currentCost = m - counts[col][val]; int futureCost = solve(col + 1, val); if (futureCost != Integer.MAX_VALUE) { minOps = Math.min(minOps, currentCost + futureCost); } } } return minOps; }}Complexity
Time
O(m*n + 10 * 9^(n-1)). The pre-computation is `O(m*n)`. The recursion tree has a depth of `n`. The root has 10 children, and each subsequent node has 9 children. This leads to `10 * 9^(n-1)` paths. This is exponential and too slow for the given constraints.
Space
O(n) for the recursion stack depth, plus O(n) for the `counts` array. Total O(n).
Trade-offs
Pros
Simple to understand and implement the logic directly from the problem definition.
Cons
Highly inefficient due to re-computing results for the same subproblems (e.g.,
solve(col, val)is called many times with the same arguments).Leads to Time Limit Exceeded for the given constraints.
The brute-force recursive approach suffers from re-calculating the same subproblems repeatedly. We can observe that the optimal cost for the first j columns, with column j having value x, only depends on the optimal costs for the first j-1 columns. This is a classic dynamic programming problem.
We can build a dp table of size n x 10, where dp[j][x] represents the minimum number of operations to satisfy the conditions for columns 0 through j, with column j being assigned the value x. The base case is column 0, where dp[0][x] is simply the cost to make the entire column x. For subsequent columns j, dp[j][x] is calculated by adding the cost for column j to the minimum possible cost from column j-1, ensuring the value chosen for column j-1 was not x.
Algorithm
- Pre-calculate the frequency of each number (0-9) for each column in a
counts[n][10]array. - Create a DP table
dp[n][10].dp[j][x]will store the minimum cost for the firstj+1columns (0 toj), with columnjhaving valuex. - Base Case (j=0): For each value
xfrom 0 to 9,dp[0][x] = m - counts[0][x]. - Transitions (j=1 to n-1): For each column
j:- To optimize, first find the two smallest costs from the previous column's DP states (
dp[j-1]), let's call themmin1andmin2, and the valueval1that producedmin1. - For each possible value
xfor columnj(0-9):cost_j_x = m - counts[j][x].- If
x != val1,dp[j][x] = cost_j_x + min1. - If
x == val1,dp[j][x] = cost_j_x + min2.
- To optimize, first find the two smallest costs from the previous column's DP states (
- Result: The minimum value in the last row of the DP table:
min(dp[n-1][x])forxfrom 0 to 9.
Walkthrough
class Solution { public int minimumOperations(int[][] grid) { int m = grid.length; int n = grid[0].length; int[][] counts = new int[n][10]; for (int j = 0; j < n; j++) { for (int i = 0; i < m; i++) { counts[j][grid[i][j]]++; } } int[][] dp = new int[n][10]; // Base case: column 0 for (int val = 0; val <= 9; val++) { dp[0][val] = m - counts[0][val]; } // Fill DP table for columns 1 to n-1 for (int j = 1; j < n; j++) { // Find two smallest costs from previous column int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; int val1 = -1; for (int prevVal = 0; prevVal <= 9; prevVal++) { if (dp[j - 1][prevVal] < min1) { min2 = min1; min1 = dp[j - 1][prevVal]; val1 = prevVal; } else if (dp[j - 1][prevVal] < min2) { min2 = dp[j - 1][prevVal]; } } for (int val = 0; val <= 9; val++) { int cost = m - counts[j][val]; if (val != val1) { dp[j][val] = cost + min1; } else { dp[j][val] = cost + min2; } } } // Find the minimum operations for the entire grid int minOps = Integer.MAX_VALUE; for (int val = 0; val <= 9; val++) { minOps = Math.min(minOps, dp[n - 1][val]); } return minOps; }}Complexity
Time
O(m * n). The pre-computation of `counts` takes `O(m * n)`. The DP calculation is `O(n * 10) = O(n)`. The total time is dominated by the pre-computation.
Space
O(n). We use O(n * 10) for the `counts` array and O(n * 10) for the `dp` table.
Trade-offs
Pros
Much more efficient than brute force.
Guaranteed to find the optimal solution in polynomial time.
Cons
Uses extra space proportional to the number of columns
n.
The dynamic programming solution is efficient in time, but its space complexity can be improved. When calculating the DP values for column j, we only need the results from the immediate previous column, j-1. We don't need the results from columns j-2, j-3, etc.
This observation allows us to optimize the space complexity. Instead of storing the entire dp[n][10] table, we only need to maintain two arrays of size 10: one for the DP values of the previous column (prev_dp) and one for the current column (curr_dp).
Furthermore, we can also optimize the space used for storing the frequency counts. Instead of pre-calculating and storing counts for all columns at once, we can calculate the counts for one column at a time as we iterate through them. This reduces the space for counts from O(n) to O(1).
Algorithm
- Initialize a
prev_dparray of size 10. This will store the minimum operations for the grid up to the previous column. - Column 0:
- Calculate the frequencies of numbers 0-9 for column 0.
- For each value
xfrom 0 to 9, initializeprev_dp[x] = m - counts_for_col_0[x].
- Columns 1 to n-1:
- Initialize a
curr_dparray of size 10. - In a loop for
jfrom 1 ton-1:- Calculate the frequencies of numbers 0-9 for the current column
j. - Find the two smallest values (
min1,min2) and the value corresponding tomin1(val1) in theprev_dparray. - For each value
xfrom 0 to 9:- Calculate the cost to change column
jtox:cost = m - counts_for_col_j[x]. - If
x != val1,curr_dp[x] = cost + min1. - Otherwise (
x == val1),curr_dp[x] = cost + min2.
- Calculate the cost to change column
- After computing
curr_dp, updateprev_dp = curr_dpfor the next iteration.
- Calculate the frequencies of numbers 0-9 for the current column
- Initialize a
- Result: After the loop finishes, the
prev_dparray holds the DP values for the last column. The minimum value in this array is the final answer.
Walkthrough
import java.util.Arrays; class Solution { public int minimumOperations(int[][] grid) { int m = grid.length; int n = grid[0].length; int[] prevDp = new int[10]; // Process column 0 int[] counts = new int[10]; for (int i = 0; i < m; i++) { counts[grid[i][0]]++; } for (int val = 0; val <= 9; val++) { prevDp[val] = m - counts[val]; } // Process columns 1 to n-1 for (int j = 1; j < n; j++) { int[] currDp = new int[10]; // Find two smallest costs from previous column's DP results int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; int val1 = -1; for (int prevVal = 0; prevVal <= 9; prevVal++) { if (prevDp[prevVal] < min1) { min2 = min1; min1 = prevDp[prevVal]; val1 = prevVal; } else if (prevDp[prevVal] < min2) { min2 = prevDp[prevVal]; } } // Calculate counts for the current column Arrays.fill(counts, 0); for (int i = 0; i < m; i++) { counts[grid[i][j]]++; } // Calculate current column's DP results for (int val = 0; val <= 9; val++) { int cost = m - counts[val]; if (val != val1) { currDp[val] = cost + min1; } else { currDp[val] = cost + min2; } } prevDp = currDp; } // Find the minimum operations for the entire grid int minOps = Integer.MAX_VALUE; for (int val = 0; val <= 9; val++) { minOps = Math.min(minOps, prevDp[val]); } return minOps; }}Complexity
Time
O(m * n). We iterate through each column `j` from 0 to `n-1`. For each column, we iterate through its `m` rows to calculate frequencies (`O(m)`). Then we perform constant time work (`O(10)`) for the DP state updates. The total time is `O(n * (m + 10)) = O(m * n)`.
Space
O(1). We use a few arrays of size 10 (`prev_dp`, `curr_dp`, `counts`), which is constant space. We are not storing information that scales with `m` or `n`.
Trade-offs
Pros
Most efficient approach in both time and space.
Solves the problem with optimal time complexity while using only constant extra space.
Cons
The logic is slightly more complex to follow than the standard DP approach due to the space optimization.
Solutions
Solution
class Solution {public int minimumOperations(int[][] grid) { int m = grid.length, n = grid[0].length; int[][] f = new int[n][10]; final int inf = 1 << 29; for (var g : f) { Arrays.fill(g, inf); } for (int i = 0; i < n; ++i) { int[] cnt = new int[10]; for (int j = 0; j < m; ++j) { ++cnt[grid[j][i]]; } if (i == 0) { for (int j = 0; j < 10; ++j) { f[i][j] = m - cnt[j]; } } else { for (int j = 0; j < 10; ++j) { for (int k = 0; k < 10; ++k) { if (k != j) { f[i][j] = Math.min(f[i][j], f[i - 1][k] + m - cnt[j]); } } } } } int ans = inf; for (int j = 0; j < 10; ++j) { ans = Math.min(ans, f[n - 1][j]); } return ans; }}class Solution {public: int minimumOperations(vector<vector<int>> &grid) { int m = grid.size(), n = grid[0].size(); int f[n][10]; memset(f, 0x3f, sizeof(f)); for (int i = 0; i < n; ++i) { int cnt[10]{}; for (int j = 0; j < m; ++j) { ++cnt[grid[j][i]]; } if (i == 0) { for (int j = 0; j < 10; ++j) { f[i][j] = m - cnt[j]; } } else { for (int j = 0; j < 10; ++j) { for (int k = 0; k < 10; ++k) { if (k != j) { f[i][j] = min(f[i][j], f[i - 1][k] + m - cnt[j]); } } } } } return *min_element(f[n - 1], f[n - 1] + 10); }};class Solution: def minimumOperations(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) f = [[inf] * 10 for _ in range(n)] for i in range(n): cnt = [0] * 10 for j in range(m): cnt[grid[j][i]] += 1 if i == 0: for j in range(10): f[i][j] = m - cnt[j] else: for j in range(10): for k in range(10): if k != j: f[i][j] = min(f[i][j], f[i - 1][k] + m - cnt[j]) return min(f[- 1])Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.