3122. Minimum Number of Operations to Satisfy Conditions
Problem Description
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.
Intuition
The goal is to transform the grid such that each column contains the same number and each row has distinct consecutive columns. Given that there are only 10 potential values (0 to 9) for each cell, we can use dynamic programming to efficiently find the solution.
-
Identify State: Define
f[i][j]
as the minimum number of operations required to make the first[0,..,i]
columns valid, with thei-th
column being filled with the numberj
. -
Base Case: For the first column (
i=0
), we computef[0][j]
asm - cnt[j]
, wherecnt[j]
is the count of already existing cells in the columni=0
with numberj
. -
State Transition: For each subsequent column
i
, calculatef[i][j]
as the minimum over all previous column configurations that have a different number, plus the cost to make the entire columni
equal toj
. This can be expressed as:f[i][j] = min(f[i-1][k] + m - cnt[j]) for all k ≠ j
Here,
m - cnt[j]
represents the number of changes needed for columni
to make all elementsj
. -
Result: After filling out the dynamic programming table, the solution is the minimum value of
f[n-1][j]
, representing the minimum operations needed for the entire grid.
By exploring all possible column configurations while ensuring distinct column transformations, this method effectively minimizes the required operations.
Learn more about Dynamic Programming patterns.
Solution Approach
To solve this problem optimally, we employ a dynamic programming approach, which is well-suited for minimizing operations while considering constraints across rows and columns:
-
Initialize the DP Table: Create a 2D list
f
of size[n][10]
, wheren
is the number of columns, and each entry is initialized to infinity (inf
). This table will store the minimum operations required to configure columns up to indexi
such that thei-th
column is uniform with the valuej
.f = [[inf] * 10 for _ in range(n)]
-
Fill the First Column: For the first column, calculate and assign the number of changes necessary for each possible number
j
(from 0 to 9) to become uniform in that column.for j in range(10): f[0][j] = m - cnt[j]
Here,
cnt[j]
counts how many times the numberj
already appears in the current column.m - cnt[j]
gives the operations needed to make the whole columnj
. -
Iterate Through Columns: For each subsequent column
i
, calculate the minimum operations required to make the column uniform with each numberj
considering different numbers in the previous column.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])
The idea is to transition states only when adjacent columns have distinct values (
k ≠ j
). This guarantees the columns adhere to the prescribed conditions. -
Compute the Final Result: Once the DP table is filled, the problem's answer is the minimum value found in the last column’s entries of table
f
.return min(f[n-1])
This solution efficiently leverages dynamic programming to track the least number of operation changes necessary while also ensuring the distinctness condition between consecutive columns is maintained.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach. Suppose we have the following grid of size 3 x 3
:
grid = [ [2, 3, 1], [2, 3, 1], [2, 3, 1] ]
Our task is to transform this grid so that:
- Each column contains the same number.
- Each row has distinct consecutive columns.
Step-by-Step Solution:
-
Initialize the DP Table:
We create a DP table
f
of size[3][10]
(since the grid has 3 columns, and each cell value can be between 0 and 9). Initialize each element to infinity (inf
).f = [[inf] * 10 for _ in range(3)]
-
Fill the First Column:
For the first column, we calculate the minimum operations needed to transform it into a uniform column for each number
j
from 0 to 9.Calculate
cnt[j]
, the count ofj
in the first column, which in this case givescnt[2] = 3
and others are 0. Thus:for j in range(10): f[0][j] = 3 - cnt[j]
Here,
f[0][2] = 0
because column 0 is already uniform with value 2. All others would require 3 changes. -
Iterate Through Columns:
Move to the second column, calculating minimum operations for each value
j
with distinct values from the first column:for i in range(1, 3): for j in range(10): for k in range(10): if k != j: f[i][j] = min(f[i][j], f[i - 1][k] + 3 - cnt[j])
Here,
cnt[3] = 3
for column 1, and similar calculations are made. -
Compute the Final Result:
After processing all columns, the minimum value in the last column gives the minimum operations needed:
result = min(f[2])
Let’s assume after computation,
result = 0
, indicating that grid configurations already satisfy the conditions without further operations (this would change based on grid initializations).
This walkthrough demonstrates the dynamic programming approach to achieve the desired grid configuration, minimizing operation count while maintaining necessary distinctness between rows and columns.
Solution Implementation
1from typing import List
2from math import inf
3
4class Solution:
5 def minimumOperations(self, grid: List[List[int]]) -> int:
6 # Determine the dimensions of the grid
7 rows, cols = len(grid), len(grid[0])
8 # Initialize a 2D array to store the minimum operations with 'inf' values
9 dp = [[inf] * 10 for _ in range(cols)]
10
11 # Iterate through each column in the grid
12 for col in range(cols):
13 # Count the frequency of each digit (0-9) in the current column
14 digit_count = [0] * 10
15 for row in range(rows):
16 digit_count[grid[row][col]] += 1
17
18 # If it's the first column, calculate the operations needed for each digit
19 if col == 0:
20 for digit in range(10):
21 dp[col][digit] = rows - digit_count[digit]
22 else:
23 # Calculate the minimum operations needed for each digit by considering different previous digit values
24 for current_digit in range(10):
25 for previous_digit in range(10):
26 if current_digit != previous_digit:
27 new_operations = dp[col - 1][previous_digit] + rows - digit_count[current_digit]
28 dp[col][current_digit] = min(dp[col][current_digit], new_operations)
29
30 # Return the minimum operations required in the last column for any digit
31 return min(dp[-1])
32
1class Solution {
2 public int minimumOperations(int[][] grid) {
3 int numRows = grid.length;
4 int numCols = grid[0].length;
5
6 // DP array to store the minimum operations required for each column with each digit
7 int[][] dp = new int[numCols][10];
8 final int INF = 1 << 29; // A large number representing infinity
9
10 // Initialize DP array with infinity values
11 for (int[] row : dp) {
12 Arrays.fill(row, INF);
13 }
14
15 // Iterate over each column
16 for (int col = 0; col < numCols; ++col) {
17 // Array to count occurrences of each digit (0-9) in the current column
18 int[] digitCount = new int[10];
19
20 // Count digits in each row for the current column
21 for (int row = 0; row < numRows; ++row) {
22 ++digitCount[grid[row][col]];
23 }
24
25 if (col == 0) {
26 // For the first column, calculate the cost of converting all entries to each digit
27 for (int digit = 0; digit < 10; ++digit) {
28 dp[col][digit] = numRows - digitCount[digit]; // Cost to convert the column to this digit
29 }
30 } else {
31 // For subsequent columns, calculate minimum cost considering the previous column
32 for (int currentDigit = 0; currentDigit < 10; ++currentDigit) {
33 for (int prevDigit = 0; prevDigit < 10; ++prevDigit) {
34 if (prevDigit != currentDigit) { // Ensure no two adjacent columns have the same digit
35 dp[col][currentDigit] = Math.min(dp[col][currentDigit],
36 dp[col - 1][prevDigit] + numRows - digitCount[currentDigit]);
37 }
38 }
39 }
40 }
41 }
42
43 // Determine the minimum operations needed from the last column across all digit options
44 int minOperations = INF;
45 for (int digit = 0; digit < 10; ++digit) {
46 minOperations = Math.min(minOperations, dp[numCols - 1][digit]);
47 }
48
49 return minOperations;
50 }
51}
52
1#include <vector>
2#include <cstring>
3#include <algorithm>
4
5class Solution {
6public:
7 int minimumOperations(std::vector<std::vector<int>>& grid) {
8 int rows = grid.size(); // Number of rows in the grid
9 int cols = grid[0].size(); // Number of columns in the grid
10
11 int dp[cols][10]; // Dynamic programming array to store the minimum operations
12 std::memset(dp, 0x3f, sizeof(dp)); // Initialize array with large values (infinity)
13
14 for (int col = 0; col < cols; ++col) {
15 int count[10] = {}; // Count of each digit (0-9) in the current column
16
17 // Count occurrences of each digit in the column
18 for (int row = 0; row < rows; ++row) {
19 ++count[grid[row][col]];
20 }
21
22 if (col == 0) { // Base case: first column
23 for (int digit = 0; digit < 10; ++digit) {
24 dp[col][digit] = rows - count[digit]; // Operations needed in the first column
25 }
26 } else { // For subsequent columns
27 for (int current_digit = 0; current_digit < 10; ++current_digit) {
28 for (int previous_digit = 0; previous_digit < 10; ++previous_digit) {
29 if (current_digit != previous_digit) {
30 // Calculate minimum operations while ensuring no same adjacent column-digit
31 dp[col][current_digit] = std::min(
32 dp[col][current_digit],
33 dp[col - 1][previous_digit] + rows - count[current_digit]
34 );
35 }
36 }
37 }
38 }
39 }
40
41 // Find the minimum operations required for the last column
42 return *std::min_element(dp[cols - 1], dp[cols - 1] + 10);
43 }
44};
45
1function minimumOperations(grid: number[][]): number {
2 const m = grid.length; // Number of rows in the grid
3 const n = grid[0].length; // Number of columns in the grid
4
5 // f[i][j] represents the minimum number of operations required to transform
6 // the first i columns such that the last column is filled with digit j
7 const f: number[][] = Array.from({ length: n }, () => Array(10).fill(Infinity));
8
9 // Iterate over each column
10 for (let i = 0; i < n; ++i) {
11 const cnt: number[] = Array(10).fill(0); // Count of each digit in the column
12
13 // Populate cnt with the count of each digit in the current column
14 for (let j = 0; j < m; ++j) {
15 cnt[grid[j][i]]++;
16 }
17
18 if (i === 0) {
19 // For the first column, calculate the number of changes needed for each digit
20 for (let j = 0; j < 10; ++j) {
21 f[i][j] = m - cnt[j]; // Calculate operations needed to set the column to digit j
22 }
23 } else {
24 // For subsequent columns, compute minimum operations ensuring different digits
25 for (let j = 0; j < 10; ++j) {
26 for (let k = 0; k < 10; ++k) {
27 if (j !== k) { // Ensure the digit changes
28 // Update f[i][j] with the minimum operations considering the previous column
29 f[i][j] = Math.min(f[i][j], f[i - 1][k] + m - cnt[j]);
30 }
31 }
32 }
33 }
34 }
35
36 // Return the minimum value from the last column of f, which gives the least operations
37 return Math.min(...f[n - 1]);
38}
39
Time and Space Complexity
The time complexity of the code is O(n * (m + C^2))
. This is because for each column i
, the code calculates the frequency count of each of the 10 possible numbers in O(m)
time. It then performs operations involving iterating over each pair of possible numbers j
and k
, which takes O(C^2)
time per column, resulting in a total of O(n * (m + C^2))
. Here, m
is the number of rows, n
is the number of columns, and C
is the number of unique numbers, which is constant (C = 10
).
The space complexity is O(n * C)
because a 2D list f
with dimensions n x 10
is used to store intermediate results for each column and possible digit, resulting in a space requirement proportional to the number of columns times the constant C
.
Learn more about how to find time and space complexity quickly.
Which technique can we use to find the middle of a linked list?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!