Facebook Pixel

3071. Minimum Operations to Write the Letter Y on a Grid

MediumArrayHash TableCountingMatrix
Leetcode Link

Problem Description

You have an n x n grid where n is odd, and each cell contains a value of 0, 1, or 2.

The problem asks you to form the letter Y on the grid. The letter Y consists of three parts:

  • The diagonal from the top-left corner to the center of the grid
  • The diagonal from the top-right corner to the center of the grid
  • The vertical line from the center down to the bottom edge of the grid

To successfully write the letter Y on the grid, three conditions must be met:

  1. All cells that form the Y shape must have the same value (either all 0s, all 1s, or all 2s)
  2. All cells that are NOT part of the Y shape must have the same value (either all 0s, all 1s, or all 2s)
  3. The value used for Y cells must be different from the value used for non-Y cells

In one operation, you can change any cell's value to 0, 1, or 2. Your task is to find the minimum number of operations needed to form a valid letter Y on the grid.

For example, if you decide Y cells should all be 1 and non-Y cells should all be 2, you would need to change every cell that doesn't already match these target values. The goal is to find the combination of values for Y and non-Y cells that requires the fewest total changes.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since we need to make all Y cells have one value and all non-Y cells have a different value, and we can only use values 0, 1, or 2, there are limited combinations to consider:

  • Y cells could be 0 and non-Y cells could be 1
  • Y cells could be 0 and non-Y cells could be 2
  • Y cells could be 1 and non-Y cells could be 0
  • Y cells could be 1 and non-Y cells could be 2
  • Y cells could be 2 and non-Y cells could be 0
  • Y cells could be 2 and non-Y cells could be 1

That's only 6 possible combinations (3 choices for Y × 2 remaining choices for non-Y).

The key insight is that we want to minimize the number of changes needed. To do this efficiently, we should count how many cells of each value (0, 1, 2) already exist in the Y region and how many exist in the non-Y region.

Once we have these counts, for any combination of target values (like Y=1, non-Y=2), we can calculate the operations needed:

  • For Y cells: we need to change all cells that aren't already 1
  • For non-Y cells: we need to change all cells that aren't already 2

The total operations for a combination equals the total number of cells minus the cells that already have the correct value. In other words: total_cells - cells_already_correct_in_Y - cells_already_correct_in_non_Y.

Since the total number of cells is n * n, and if cnt1[i] represents how many Y cells already have value i, and cnt2[j] represents how many non-Y cells already have value j, then the operations needed when setting Y to i and non-Y to j is: n * n - cnt1[i] - cnt2[j].

We try all 6 valid combinations where i ≠ j and return the minimum.

Solution Approach

The solution uses a counting approach with two Counter objects to track the frequency of values in Y cells and non-Y cells separately.

Step 1: Identify Y cells

For each cell (i, j) in the grid, we determine if it belongs to the letter Y by checking three conditions:

  • a = i == j and i <= n // 2: This identifies cells on the main diagonal from top-left to center
  • b = i + j == n - 1 and i <= n // 2: This identifies cells on the anti-diagonal from top-right to center
  • c = j == n // 2 and i >= n // 2: This identifies cells on the vertical line from center to bottom

If any of these conditions is true (a or b or c), the cell belongs to Y.

Step 2: Count values in Y and non-Y regions

We iterate through the entire grid once:

  • If a cell belongs to Y, we increment cnt1[x] where x is the cell's value
  • If a cell doesn't belong to Y, we increment cnt2[x] where x is the cell's value

After this pass, cnt1 contains the count of each value (0, 1, 2) in Y cells, and cnt2 contains the count of each value in non-Y cells.

Step 3: Find minimum operations

We try all valid combinations where Y cells have value i and non-Y cells have value j (where i ≠ j):

  • For each combination, the number of operations needed is n * n - cnt1[i] - cnt2[j]
  • n * n is the total number of cells
  • cnt1[i] is the number of Y cells already having the target value i
  • cnt2[j] is the number of non-Y cells already having the target value j
  • The difference gives us how many cells need to be changed

The final answer is the minimum across all valid combinations: min(n * n - cnt1[i] - cnt2[j] for i in range(3) for j in range(3) if i != j).

This approach is efficient with O(n²) time complexity for counting and O(1) space for the counters (since we only have 3 possible values).

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with a 3×3 grid:

Initial grid:
2 1 2
1 1 2
2 0 1

Step 1: Identify Y cells

For a 3×3 grid (n=3), the Y shape consists of:

  • Main diagonal from top-left to center: (0,0), (1,1)
  • Anti-diagonal from top-right to center: (0,2), (1,1)
  • Vertical line from center to bottom: (1,1), (2,1)

Note that (1,1) is the center and appears in all three parts.

So the Y cells are at positions: (0,0), (0,2), (1,1), (2,1)

Y cells marked with *:
*2  1  *2
 1 *1   2
 2 *0   1

Step 2: Count values in Y and non-Y regions

Y cells and their values:

  • (0,0) = 2
  • (0,2) = 2
  • (1,1) = 1
  • (2,1) = 0

Y cell counts (cnt1):

  • Value 0: 1 cell
  • Value 1: 1 cell
  • Value 2: 2 cells

Non-Y cells and their values:

  • (0,1) = 1
  • (1,0) = 1
  • (1,2) = 2
  • (2,0) = 2
  • (2,2) = 1

Non-Y cell counts (cnt2):

  • Value 0: 0 cells
  • Value 1: 3 cells
  • Value 2: 2 cells

Step 3: Calculate operations for each valid combination

Total cells = 3 × 3 = 9

For each combination where Y value ≠ non-Y value:

  1. Y=0, non-Y=1: Operations = 9 - cnt1[0] - cnt2[1] = 9 - 1 - 3 = 5
  2. Y=0, non-Y=2: Operations = 9 - cnt1[0] - cnt2[2] = 9 - 1 - 2 = 6
  3. Y=1, non-Y=0: Operations = 9 - cnt1[1] - cnt2[0] = 9 - 1 - 0 = 8
  4. Y=1, non-Y=2: Operations = 9 - cnt1[1] - cnt2[2] = 9 - 1 - 2 = 6
  5. Y=2, non-Y=0: Operations = 9 - cnt1[2] - cnt2[0] = 9 - 2 - 0 = 7
  6. Y=2, non-Y=1: Operations = 9 - cnt1[2] - cnt2[1] = 9 - 2 - 3 = 4

The minimum is 4 operations, achieved by setting Y cells to value 2 and non-Y cells to value 1.

Verification of the optimal solution:

Starting grid and needed changes for Y=2, non-Y=1:

  • (0,0)=2 → 2 (no change, already correct)
  • (0,1)=1 → 1 (no change, already correct)
  • (0,2)=2 → 2 (no change, already correct)
  • (1,0)=1 → 1 (no change, already correct)
  • (1,1)=1 → 2 (change needed)
  • (1,2)=2 → 1 (change needed)
  • (2,0)=2 → 1 (change needed)
  • (2,1)=0 → 2 (change needed)
  • (2,2)=1 → 1 (no change, already correct)

Total changes needed: 4, which matches our calculation.

Solution Implementation

1class Solution:
2    def minimumOperationsToWriteY(self, grid: List[List[int]]) -> int:
3        n = len(grid)
4      
5        # Counter for cells that form the Y shape
6        y_cells_counter = Counter()
7        # Counter for cells outside the Y shape
8        non_y_cells_counter = Counter()
9      
10        # Iterate through the grid to classify each cell
11        for row_idx, row in enumerate(grid):
12            for col_idx, cell_value in enumerate(row):
13                # Check if current cell is part of the Y shape
14              
15                # Left diagonal of Y (top-left to center)
16                is_left_diagonal = (row_idx == col_idx and row_idx <= n // 2)
17              
18                # Right diagonal of Y (top-right to center)
19                is_right_diagonal = (row_idx + col_idx == n - 1 and row_idx <= n // 2)
20              
21                # Vertical stem of Y (center to bottom)
22                is_vertical_stem = (col_idx == n // 2 and row_idx >= n // 2)
23              
24                # If cell belongs to Y shape, count it in y_cells_counter
25                if is_left_diagonal or is_right_diagonal or is_vertical_stem:
26                    y_cells_counter[cell_value] += 1
27                else:
28                    # Otherwise, count it in non_y_cells_counter
29                    non_y_cells_counter[cell_value] += 1
30      
31        # Calculate minimum operations needed
32        # Try all combinations where Y cells have color i and non-Y cells have color j (i != j)
33        total_cells = n * n
34        min_operations = min(
35            total_cells - y_cells_counter[y_color] - non_y_cells_counter[non_y_color]
36            for y_color in range(3) 
37            for non_y_color in range(3) 
38            if y_color != non_y_color
39        )
40      
41        return min_operations
42
1class Solution {
2    public int minimumOperationsToWriteY(int[][] grid) {
3        int gridSize = grid.length;
4      
5        // Count frequency of values (0, 1, or 2) in Y-shape and non-Y cells
6        int[] yPatternCounts = new int[3];
7        int[] nonYPatternCounts = new int[3];
8      
9        // Iterate through entire grid
10        for (int row = 0; row < gridSize; ++row) {
11            for (int col = 0; col < gridSize; ++col) {
12                // Check if current cell belongs to the Y pattern
13                boolean isOnLeftDiagonal = (row == col) && (row <= gridSize / 2);
14                boolean isOnRightDiagonal = (row + col == gridSize - 1) && (row <= gridSize / 2);
15                boolean isOnVerticalStem = (col == gridSize / 2) && (row >= gridSize / 2);
16              
17                if (isOnLeftDiagonal || isOnRightDiagonal || isOnVerticalStem) {
18                    // Cell is part of Y pattern
19                    ++yPatternCounts[grid[row][col]];
20                } else {
21                    // Cell is not part of Y pattern
22                    ++nonYPatternCounts[grid[row][col]];
23                }
24            }
25        }
26      
27        // Find minimum operations needed
28        int totalCells = gridSize * gridSize;
29        int minimumOperations = totalCells;
30      
31        // Try all combinations of values for Y pattern (i) and non-Y cells (j)
32        for (int yValue = 0; yValue < 3; ++yValue) {
33            for (int nonYValue = 0; nonYValue < 3; ++nonYValue) {
34                // Y pattern and non-Y cells must have different values
35                if (yValue != nonYValue) {
36                    // Operations needed = total cells - cells already having correct values
37                    int operationsNeeded = totalCells - yPatternCounts[yValue] - nonYPatternCounts[nonYValue];
38                    minimumOperations = Math.min(minimumOperations, operationsNeeded);
39                }
40            }
41        }
42      
43        return minimumOperations;
44    }
45}
46
1class Solution {
2public:
3    int minimumOperationsToWriteY(vector<vector<int>>& grid) {
4        int n = grid.size();
5      
6        // Count frequency of values (0, 1, 2) in Y pattern and non-Y cells
7        int yPatternCount[3] = {0};      // Frequency of each value in Y pattern
8        int nonYPatternCount[3] = {0};   // Frequency of each value outside Y pattern
9      
10        // Iterate through the entire grid
11        for (int row = 0; row < n; ++row) {
12            for (int col = 0; col < n; ++col) {
13                // Check if current cell belongs to Y pattern
14                // Y pattern consists of three parts:
15              
16                // Part 1: Main diagonal from top-left (first half)
17                bool isMainDiagonal = (row == col) && (row <= n / 2);
18              
19                // Part 2: Anti-diagonal from top-right (first half)
20                bool isAntiDiagonal = (row + col == n - 1) && (row <= n / 2);
21              
22                // Part 3: Vertical line from center to bottom
23                bool isVerticalStem = (col == n / 2) && (row >= n / 2);
24              
25                // If cell is part of Y pattern
26                if (isMainDiagonal || isAntiDiagonal || isVerticalStem) {
27                    ++yPatternCount[grid[row][col]];
28                } else {
29                    // Cell is not part of Y pattern
30                    ++nonYPatternCount[grid[row][col]];
31                }
32            }
33        }
34      
35        // Find minimum operations by trying all combinations
36        int minOperations = n * n;  // Initialize with maximum possible operations
37      
38        // Try all possible value assignments
39        // i: value to assign to Y pattern cells
40        // j: value to assign to non-Y pattern cells
41        for (int yValue = 0; yValue < 3; ++yValue) {
42            for (int nonYValue = 0; nonYValue < 3; ++nonYValue) {
43                // Y pattern and non-Y pattern must have different values
44                if (yValue != nonYValue) {
45                    // Calculate operations needed for this combination
46                    // Operations = total cells - cells already having correct value
47                    int currentOperations = n * n - yPatternCount[yValue] - nonYPatternCount[nonYValue];
48                    minOperations = min(minOperations, currentOperations);
49                }
50            }
51        }
52      
53        return minOperations;
54    }
55};
56
1/**
2 * Calculates the minimum number of operations needed to write the letter Y on a grid
3 * @param grid - A square grid containing values 0, 1, or 2
4 * @returns The minimum number of operations required
5 */
6function minimumOperationsToWriteY(grid: number[][]): number {
7    const gridSize: number = grid.length;
8  
9    // Count frequency of values (0, 1, 2) in Y-shaped cells
10    const yPatternCounts: number[] = Array(3).fill(0);
11  
12    // Count frequency of values (0, 1, 2) in non-Y cells
13    const nonYPatternCounts: number[] = Array(3).fill(0);
14  
15    // Iterate through the entire grid
16    for (let row = 0; row < gridSize; ++row) {
17        for (let col = 0; col < gridSize; ++col) {
18            // Check if current cell is part of the Y pattern
19          
20            // Left diagonal of Y (top-left to center)
21            const isLeftDiagonal: boolean = row === col && row <= gridSize >> 1;
22          
23            // Right diagonal of Y (top-right to center)
24            const isRightDiagonal: boolean = row + col === gridSize - 1 && row <= gridSize >> 1;
25          
26            // Vertical stem of Y (center to bottom)
27            const isVerticalStem: boolean = col === gridSize >> 1 && row >= gridSize >> 1;
28          
29            // Determine if cell belongs to Y pattern or not
30            if (isLeftDiagonal || isRightDiagonal || isVerticalStem) {
31                // Increment count for the value in Y pattern
32                ++yPatternCounts[grid[row][col]];
33            } else {
34                // Increment count for the value in non-Y pattern
35                ++nonYPatternCounts[grid[row][col]];
36            }
37        }
38    }
39  
40    // Initialize minimum operations to maximum possible value
41    let minOperations: number = gridSize * gridSize;
42  
43    // Try all combinations of colors for Y pattern and non-Y cells
44    for (let yColor = 0; yColor < 3; ++yColor) {
45        for (let nonYColor = 0; nonYColor < 3; ++nonYColor) {
46            // Y pattern and non-Y cells must have different colors
47            if (yColor !== nonYColor) {
48                // Calculate operations needed: total cells minus cells already having target colors
49                const operationsNeeded: number = gridSize * gridSize - yPatternCounts[yColor] - nonYPatternCounts[nonYColor];
50                minOperations = Math.min(minOperations, operationsNeeded);
51            }
52        }
53    }
54  
55    return minOperations;
56}
57

Time and Space Complexity

Time Complexity: O(n²)

The algorithm iterates through the entire grid once using nested loops (enumerate over rows and columns), which takes O(n²) time where n is the size of the matrix. The conditions checking (a, b, c) and counter updates are all O(1) operations. The final minimum calculation iterates through at most 3×3 = 9 combinations, which is O(1). Therefore, the overall time complexity is dominated by the grid traversal, resulting in O(n²).

Space Complexity: O(1)

The algorithm uses two Counter objects (cnt1 and cnt2) to store the frequency of values. Since the grid values are restricted to 0, 1, or 2 (as implied by the range(3) in the final calculation), each counter can have at most 3 key-value pairs. This means the space used by the counters is constant and doesn't depend on the input size n. The loop variables and temporary variables also use constant space. Therefore, the space complexity is O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Y Shape Definition

Pitfall: A common mistake is incorrectly defining which cells belong to the Y shape, particularly at the boundaries where the diagonals meet the vertical stem.

For instance, developers might accidentally include cells beyond the center point in the diagonals, or forget that the center cell (n//2, n//2) belongs to all three parts of the Y and should only be counted once.

Solution: Carefully define the conditions:

  • Left diagonal: row == col AND row <= n//2 (stops at center)
  • Right diagonal: row + col == n-1 AND row <= n//2 (stops at center)
  • Vertical stem: col == n//2 AND row >= n//2 (starts from center)

The center cell at position (n//2, n//2) satisfies all three conditions but is correctly counted only once due to the OR logic.

2. Forgetting to Initialize Counters for Missing Values

Pitfall: When using Counter(), if a particular value (0, 1, or 2) doesn't appear in either Y cells or non-Y cells, accessing it directly returns 0. However, some developers might assume all values are present and try alternative counting methods that could fail.

Solution: The current implementation handles this correctly because Counter objects return 0 for missing keys. When calculating total_cells - y_cells_counter[y_color] - non_y_cells_counter[non_y_color], if a color doesn't exist in one region, its count is automatically 0.

3. Off-by-One Errors with Grid Center

Pitfall: For odd-sized grids, integer division to find the center can be confusing. Some might use (n-1)//2 instead of n//2, leading to an incorrect Y shape.

Solution: For an n×n grid where n is odd, the center is always at index n//2. For example:

  • n=3: center at index 1 (positions 0,1,2)
  • n=5: center at index 2 (positions 0,1,2,3,4)

4. Attempting to Optimize Prematurely

Pitfall: Trying to track only the maximum count in each region instead of all counts, thinking it would be more efficient. This approach fails because you need to know the exact count of each value to calculate operations for all valid combinations.

Solution: Use complete frequency counting for both regions. While it might seem like extra work, you need all counts to properly evaluate each of the 6 possible valid combinations (3 choices for Y × 2 remaining choices for non-Y).

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More