Facebook Pixel

2647. Color the Triangle Red 🔒

Problem Description

You have an equilateral triangle with side length n that is divided into smaller unit equilateral triangles. The large triangle has n rows (indexed from 1), where row i contains 2i - 1 unit triangles. Each unit triangle is identified by coordinates (i, j) where i is the row number and j ranges from 1 to 2i - 1.

Two unit triangles are considered neighbors if they share a common side. For example:

  • Triangles (1,1) and (2,2) are neighbors
  • Triangles (3,2) and (3,3) are neighbors
  • Triangles (2,2) and (3,3) are NOT neighbors (they don't share a side)

Initially, all unit triangles are white. You need to select k triangles and color them red, then run this algorithm:

  1. Find a white triangle that has at least 2 red neighbors
    • If no such triangle exists, stop
  2. Color that triangle red
  3. Return to step 1

The goal is to find the minimum value of k and determine which k triangles to initially color red such that when the algorithm completes, all unit triangles become red.

Return a 2D list containing the coordinates [i, j] of the triangles to initially color red. If multiple valid solutions exist with the minimum k, return any one of them.

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

Intuition

To understand the pattern, let's think about how the coloring propagates. Once we color some triangles red initially, the algorithm will automatically color white triangles that have at least 2 red neighbors. This creates a chain reaction where red triangles spread throughout the entire triangle.

The key insight is that we need to strategically place the initial red triangles so that the propagation can reach every unit triangle. If we observe the structure, we notice that:

  1. The top triangle (1,1) is isolated - it only has neighbors in row 2, so it must be colored initially since it can never have 2 red neighbors from other triangles.

  2. For the remaining rows (from row n down to row 2), we need to ensure the red color can propagate both horizontally within each row and vertically between rows.

By experimenting with different patterns, we can discover that there's a repeating 4-row pattern that works efficiently:

  • In every 4th row (counting from the bottom), we color alternating triangles: positions 1, 3, 5, ...
  • In the next row up, we color just one triangle at position 2
  • In the next row up, we color alternating triangles starting from position 3: positions 3, 5, 7, ...
  • In the next row up, we color just one triangle at position 1

This pattern ensures that:

  1. Each row gets enough "seed" triangles to trigger propagation
  2. The colored triangles in adjacent rows are positioned to help propagate the color both within their own row and to neighboring rows
  3. The pattern repeats every 4 rows, making it a systematic approach

The algorithm works by processing rows from bottom to top (row n to row 2), applying this 4-row coloring pattern cyclically. The variable k in the solution tracks which step of the 4-row cycle we're in (0, 1, 2, or 3).

Learn more about Math patterns.

Solution Approach

Based on the pattern discovered, we implement the solution by coloring triangles in a specific order:

  1. Always color the top triangle: Start by adding [1, 1] to our answer list since this triangle must be colored initially (it can never get 2 red neighbors from other triangles).

  2. Process rows from bottom to top: We iterate from row n down to row 2, applying a 4-row repeating pattern. We use a variable k to track which step of the pattern we're in (cycles through 0, 1, 2, 3).

  3. Apply the 4-row coloring pattern:

    • When k = 0: Color all odd-positioned triangles in the row. For row i, color triangles at positions (i, 1), (i, 3), (i, 5), ..., up to (i, 2i-1). This is implemented with for j in range(1, i << 1, 2).

    • When k = 1: Color only one triangle at position 2. Add [i, 2] to the answer.

    • When k = 2: Color odd-positioned triangles starting from position 3. For row i, color triangles at positions (i, 3), (i, 5), ..., up to (i, 2i-1). This is implemented with for j in range(3, i << 1, 2).

    • When k = 3: Color only one triangle at position 1. Add [i, 1] to the answer.

  4. Update the pattern counter: After processing each row, increment k and take modulo 4 to cycle through the pattern: k = (k + 1) % 4.

The expression i << 1 is a bit shift operation equivalent to i * 2, giving us 2i which is one more than the maximum column index 2i - 1 for row i.

This systematic approach ensures that:

  • Every unit triangle either gets colored initially or can be reached by the propagation algorithm
  • We use the minimum number of initially colored triangles
  • The red color spreads efficiently through the triangle structure

The final answer is the list of coordinates [row, column] of all triangles that need to be colored red initially.

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 n = 5 to illustrate the solution approach.

Step 1: Visualize the triangle structure

Row 1:        (1,1)
Row 2:     (2,1) (2,2) (2,3)
Row 3:   (3,1) (3,2) (3,3) (3,4) (3,5)
Row 4: (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) (4,7)
Row 5: (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7) (5,8) (5,9)

Step 2: Apply the coloring pattern

We start with k = 0 and process from row 5 down to row 2:

  • Always color (1,1): Add [1, 1] to our answer

  • Row 5 (k = 0): Color odd positions 1, 3, 5, 7, 9

    • Add [5, 1], [5, 3], [5, 5], [5, 7], [5, 9]
    • Update: k = (0 + 1) % 4 = 1
  • Row 4 (k = 1): Color only position 2

    • Add [4, 2]
    • Update: k = (1 + 1) % 4 = 2
  • Row 3 (k = 2): Color odd positions starting from 3 → positions 3, 5

    • Add [3, 3], [3, 5]
    • Update: k = (2 + 1) % 4 = 3
  • Row 2 (k = 3): Color only position 1

    • Add [2, 1]
    • Update: k = (3 + 1) % 4 = 0

Step 3: Initial red triangles

Row 1:        [R]           (1,1) is red
Row 2:     [R] [ ] [ ]       (2,1) is red
Row 3:   [ ] [ ] [R] [ ] [R] (3,3) and (3,5) are red
Row 4: [ ] [R] [ ] [ ] [ ] [ ] [ ]  (4,2) is red
Row 5: [R] [ ] [R] [ ] [R] [ ] [R] [ ] [R]  (5,1), (5,3), (5,5), (5,7), (5,9) are red

Step 4: Verify propagation works

Now let's verify a few propagation steps:

  • (5,2) has red neighbors (5,1) and (5,3) → becomes red
  • (5,4) has red neighbors (5,3) and (5,5) → becomes red
  • (4,1) has red neighbors (5,1) and (5,2) → becomes red
  • (3,1) has red neighbors (4,1) and (4,2) → becomes red
  • (2,2) has red neighbors (1,1) and (3,3) → becomes red

This chain reaction continues until all triangles are colored red.

Final answer: [[1,1], [5,1], [5,3], [5,5], [5,7], [5,9], [4,2], [3,3], [3,5], [2,1]]

Total initially colored triangles: k = 10

Solution Implementation

1class Solution:
2    def colorRed(self, n: int) -> List[List[int]]:
3        # Initialize result with the starting coordinate [1, 1]
4        result = [[1, 1]]
5      
6        # Cycle counter for determining which pattern to use (0-3)
7        cycle_index = 0
8      
9        # Iterate from n down to 2 (inclusive)
10        for row in range(n, 1, -1):
11            # Case 0: Generate all odd column indices from 1 to 2*row-1
12            if cycle_index == 0:
13                for column in range(1, row << 1, 2):  # row << 1 is equivalent to row * 2
14                    result.append([row, column])
15          
16            # Case 1: Add single coordinate with column 2
17            elif cycle_index == 1:
18                result.append([row, 2])
19          
20            # Case 2: Generate odd column indices from 3 to 2*row-1
21            elif cycle_index == 2:
22                for column in range(3, row << 1, 2):  # Start from 3 instead of 1
23                    result.append([row, column])
24          
25            # Case 3: Add single coordinate with column 1
26            else:
27                result.append([row, 1])
28          
29            # Move to next case in the cycle (0 -> 1 -> 2 -> 3 -> 0)
30            cycle_index = (cycle_index + 1) % 4
31      
32        return result
33
1class Solution {
2    public int[][] colorRed(int n) {
3        // List to store coordinate pairs
4        List<int[]> result = new ArrayList<>();
5      
6        // Add the starting position (1, 1)
7        result.add(new int[] {1, 1});
8      
9        // Process rows from n down to 2, cycling through 4 different patterns
10        int patternIndex = 0;
11        for (int row = n; row > 1; row--) {
12            // Determine which pattern to apply based on cycle position
13            if (patternIndex == 0) {
14                // Pattern 0: Add all odd column positions up to 2*row - 1
15                for (int column = 1; column < row * 2; column += 2) {
16                    result.add(new int[] {row, column});
17                }
18            } else if (patternIndex == 1) {
19                // Pattern 1: Add only column 2
20                result.add(new int[] {row, 2});
21            } else if (patternIndex == 2) {
22                // Pattern 2: Add odd column positions starting from 3 up to 2*row - 1
23                for (int column = 3; column < row * 2; column += 2) {
24                    result.add(new int[] {row, column});
25                }
26            } else {
27                // Pattern 3: Add only column 1
28                result.add(new int[] {row, 1});
29            }
30          
31            // Move to next pattern in the cycle (0 -> 1 -> 2 -> 3 -> 0)
32            patternIndex = (patternIndex + 1) % 4;
33        }
34      
35        // Convert list to 2D array and return
36        return result.toArray(new int[0][]);
37    }
38}
39
1class Solution {
2public:
3    vector<vector<int>> colorRed(int n) {
4        vector<vector<int>> result;
5      
6        // Add the starting coordinate (1, 1)
7        result.push_back({1, 1});
8      
9        // Process rows from n down to 2, cycling through 4 different patterns
10        for (int row = n, patternIndex = 0; row > 1; --row, patternIndex = (patternIndex + 1) % 4) {
11          
12            if (patternIndex == 0) {
13                // Pattern 0: Add all odd column positions from 1 to (2 * row - 1)
14                for (int col = 1; col < (row << 1); col += 2) {
15                    result.push_back({row, col});
16                }
17            }
18            else if (patternIndex == 1) {
19                // Pattern 1: Add only column 2
20                result.push_back({row, 2});
21            }
22            else if (patternIndex == 2) {
23                // Pattern 2: Add odd column positions starting from 3 to (2 * row - 1)
24                for (int col = 3; col < (row << 1); col += 2) {
25                    result.push_back({row, col});
26                }
27            }
28            else {
29                // Pattern 3: Add only column 1
30                result.push_back({row, 1});
31            }
32        }
33      
34        return result;
35    }
36};
37
1/**
2 * Generates coordinates for coloring red cells in a pattern based on the given number.
3 * @param n - The starting row number for the pattern generation
4 * @returns A 2D array containing [row, column] coordinates of red cells
5 */
6function colorRed(n: number): number[][] {
7    // Initialize result array with the starting cell at position [1, 1]
8    const result: number[][] = [[1, 1]];
9  
10    // Pattern cycle counter (0-3) to determine which pattern to apply
11    let patternIndex: number = 0;
12  
13    // Iterate from row n down to row 2
14    for (let row: number = n; row > 1; row--) {
15        // Apply different patterns based on the current pattern index
16        if (patternIndex === 0) {
17            // Pattern 0: Add all odd column positions in the current row
18            // Column range: 1 to (2 * row - 1), stepping by 2
19            for (let column: number = 1; column < (row << 1); column += 2) {
20                result.push([row, column]);
21            }
22        } else if (patternIndex === 1) {
23            // Pattern 1: Add only column 2 in the current row
24            result.push([row, 2]);
25        } else if (patternIndex === 2) {
26            // Pattern 2: Add odd column positions starting from column 3
27            // Column range: 3 to (2 * row - 1), stepping by 2
28            for (let column: number = 3; column < (row << 1); column += 2) {
29                result.push([row, column]);
30            }
31        } else {
32            // Pattern 3: Add only column 1 in the current row
33            result.push([row, 1]);
34        }
35      
36        // Cycle through patterns 0-3
37        patternIndex = (patternIndex + 1) % 4;
38    }
39  
40    return result;
41}
42

Time and Space Complexity

The time complexity is O(n²), where n is the parameter given in the problem.

The algorithm iterates through values from n down to 2 (which is n-1 iterations). In each iteration:

  • When k = 0: The inner loop runs from 1 to 2*i with step 2, resulting in i iterations (since 2*i/2 = i)
  • When k = 1: Only one append operation
  • When k = 2: The inner loop runs from 3 to 2*i with step 2, resulting in approximately i-1 iterations
  • When k = 3: Only one append operation

Since the pattern repeats every 4 iterations, roughly half of the outer loop iterations will have inner loops that execute O(i) times. The total number of operations is approximately:

  • For k = 0 cases: n + (n-4) + (n-8) + ... ≈ n²/8
  • For k = 2 cases: (n-2) + (n-6) + (n-10) + ... ≈ n²/8
  • For k = 1 and k = 3 cases: O(n)

The sum gives us O(n²) time complexity.

The space complexity is O(1) when ignoring the space consumption of the answer array ans. The only extra variables used are k, i, and j, which consume constant space regardless of the input size n.

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

Common Pitfalls

1. Incorrect Understanding of Triangle Adjacency

Many developers mistakenly assume that triangles sharing a vertex are neighbors. In this problem, only triangles sharing a complete edge are neighbors. This misunderstanding leads to incorrect propagation logic and may result in some triangles never turning red.

Solution: Carefully verify neighbor relationships by checking if triangles share a full side, not just a vertex. Draw out small examples (n=2, n=3) to visualize the actual adjacency relationships.

2. Off-by-One Errors in Row/Column Indexing

The problem uses 1-based indexing where row i contains columns from 1 to 2i-1. Common mistakes include:

  • Using 0-based indexing accidentally
  • Iterating up to 2i instead of 2i-1
  • Confusing the range boundaries when generating odd-positioned triangles

Solution: Be consistent with 1-based indexing throughout. When iterating odd positions, use range(1, 2*i, 2) which correctly generates 1, 3, 5, ..., up to but not including 2*i.

3. Incorrect Pattern Cycle Management

The 4-row repeating pattern must be applied correctly from bottom to top. Common errors:

  • Starting the cycle at the wrong position (not starting with cycle_index = 0)
  • Applying the pattern top-to-bottom instead of bottom-to-top
  • Forgetting to update the cycle counter or updating it incorrectly

Solution: Initialize cycle_index = 0 before the loop, process rows from n down to 2, and update with cycle_index = (cycle_index + 1) % 4 after each row.

4. Missing the Top Triangle

The triangle at position [1, 1] must always be colored initially because it has no triangles above it to provide the required 2 red neighbors. Forgetting to include this leads to an incomplete solution.

Solution: Always start the result list with [[1, 1]] before processing other rows.

5. Using Bit Shift Without Understanding

The code uses row << 1 as an optimization for row * 2. If modified incorrectly (like row << 2 thinking it means row * 2), it produces wrong bounds.

Solution: Either stick with the clear multiplication row * 2 for readability, or understand that << 1 means shift left by 1 bit (multiply by 2). Don't modify the shift amount unless you understand binary operations.

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

Which technique can we use to find the middle of a linked list?


Recommended Readings

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

Load More