Facebook Pixel

2320. Count Number of Ways to Place Houses

Problem Description

You have a street with n * 2 plots arranged in two rows, with n plots on each side of the street. Each side's plots are numbered from 1 to n. You can place houses on these plots.

The constraint is that no two houses can be adjacent to each other on the same side of the street. Houses on opposite sides of the street (at the same position number) don't affect each other - you can place houses on both the i-th plot of side 1 and the i-th plot of side 2 simultaneously.

Your task is to find the total number of ways to place houses on all the plots while following the adjacency rule. Since the answer can be very large, return it modulo 10^9 + 7.

For example, if n = 2, you have 4 plots total (2 on each side). On each side, you could:

  • Place no houses
  • Place a house on plot 1 only
  • Place a house on plot 2 only
  • Not place houses on both plots 1 and 2 (since they would be adjacent)

Since the two sides are independent, you multiply the number of ways for one side by itself to get the total arrangements.

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

Intuition

The key insight is recognizing that the two sides of the street are completely independent - placing houses on one side doesn't affect the placement on the other side. This means we can solve for one side first, then use that result for both sides.

For a single side, this becomes a classic dynamic programming problem similar to the "House Robber" problem. At each plot position, we have two choices: place a house or don't place a house. However, if we place a house at position i, we cannot place a house at position i-1 (adjacency constraint).

This naturally leads us to track two states at each position:

  • f[i]: number of ways when we DO place a house at position i
  • g[i]: number of ways when we DON'T place a house at position i

The transitions become clear:

  • If we place a house at position i (contributing to f[i]), then position i-1 must be empty. So f[i] = g[i-1]
  • If we don't place a house at position i (contributing to g[i]), then position i-1 can be either empty or occupied. So g[i] = f[i-1] + g[i-1]

Starting with f[0] = g[0] = 1 (one way to place or not place a house at the first position), we can build up to position n-1.

The total ways for one side is f[n-1] + g[n-1] (sum of placing or not placing a house at the last position).

Since both sides are independent and identical, the final answer is (ways_for_one_side)^2 % mod.

Learn more about Dynamic Programming patterns.

Solution Approach

We implement the dynamic programming solution using two arrays f and g of size n to track the placement states:

  1. Initialize base cases: Both f[0] and g[0] are set to 1, representing one way to place or not place a house at the first plot.

  2. Build the DP arrays: For each position i from 1 to n-1:

    • f[i] = g[i-1]: If we place a house at position i, the previous position must be empty
    • g[i] = (f[i-1] + g[i-1]) % mod: If we don't place a house at position i, we sum the ways from both previous states
  3. Calculate ways for one side: After filling the arrays, the total number of ways for one side is v = f[n-1] + g[n-1], which accounts for both placing and not placing a house at the last position.

  4. Compute final answer: Since the two sides are independent, we square the result: v * v % mod

The algorithm uses O(n) space for the two arrays and O(n) time for the single loop iteration.

The modulo operation is applied during the calculation of g[i] to prevent integer overflow, and again at the final multiplication step to ensure the result stays within bounds.

This approach elegantly reduces a 2D street problem to a 1D problem by recognizing the independence between sides, then applies classic dynamic programming to count valid arrangements on a single side.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the solution with n = 3 (3 plots on each side of the street).

Step 1: Solve for one side using dynamic programming

We'll track two arrays:

  • f[i]: ways to arrange houses with a house AT position i
  • g[i]: ways to arrange houses with NO house at position i

Starting with position 0:

  • f[0] = 1 (one way to place a house at position 0)
  • g[0] = 1 (one way to not place a house at position 0)

Position 1:

  • f[1] = g[0] = 1 (if we place a house at position 1, position 0 must be empty)
  • g[1] = f[0] + g[0] = 1 + 1 = 2 (if no house at position 1, position 0 can be either)

Position 2:

  • f[2] = g[1] = 2 (if we place a house at position 2, position 1 must be empty)
  • g[2] = f[1] + g[1] = 1 + 2 = 3 (if no house at position 2, position 1 can be either)

Step 2: Calculate total ways for one side Total ways = f[2] + g[2] = 2 + 3 = 5

This represents 5 valid arrangements for one side:

  1. No houses: _ _ _
  2. House at position 0 only: H _ _
  3. House at position 1 only: _ H _
  4. House at position 2 only: _ _ H
  5. Houses at positions 0 and 2: H _ H

Note that arrangements like H H _ or _ H H are invalid due to adjacency.

Step 3: Calculate total ways for both sides Since the two sides are independent: Total ways = 5 × 5 = 25

Each of the 5 arrangements on side 1 can be paired with any of the 5 arrangements on side 2, giving us 25 total valid ways to place houses on the street.

The final answer would be 25 % (10^9 + 7) = 25.

Solution Implementation

1class Solution:
2    def countHousePlacements(self, n: int) -> int:
3        MOD = 10**9 + 7
4      
5        # dp_place[i]: number of ways when we place a house at position i
6        # dp_no_place[i]: number of ways when we don't place a house at position i
7        dp_place = [1] * n
8        dp_no_place = [1] * n
9      
10        # Build up the dynamic programming arrays
11        for i in range(1, n):
12            # If we place a house at position i, previous position must be empty
13            dp_place[i] = dp_no_place[i - 1]
14          
15            # If we don't place a house at position i, previous position can be either empty or occupied
16            dp_no_place[i] = (dp_place[i - 1] + dp_no_place[i - 1]) % MOD
17      
18        # Total ways for one side of the street
19        one_side_total = dp_place[-1] + dp_no_place[-1]
20      
21        # Since we have two sides of the street and they're independent,
22        # multiply the possibilities for both sides
23        return (one_side_total * one_side_total) % MOD
24
1class Solution {
2    public int countHousePlacements(int n) {
3        final int MOD = 1_000_000_007;
4      
5        // dp arrays for one side of the street
6        // hasHouse[i]: number of ways when plot i has a house
7        int[] hasHouse = new int[n];
8        // noHouse[i]: number of ways when plot i doesn't have a house
9        int[] noHouse = new int[n];
10      
11        // Base case: first plot can either have a house or not
12        hasHouse[0] = 1;  // One way to place a house at plot 0
13        noHouse[0] = 1;   // One way to leave plot 0 empty
14      
15        // Fill the dp arrays for remaining plots
16        for (int i = 1; i < n; i++) {
17            // If we place a house at plot i, previous plot must be empty
18            hasHouse[i] = noHouse[i - 1];
19          
20            // If plot i is empty, previous plot can either have a house or be empty
21            noHouse[i] = (hasHouse[i - 1] + noHouse[i - 1]) % MOD;
22        }
23      
24        // Total ways for one side of the street
25        long waysOneSide = (hasHouse[n - 1] + noHouse[n - 1]) % MOD;
26      
27        // Since both sides are independent, multiply the ways
28        // (ways for left side) * (ways for right side)
29        return (int) (waysOneSide * waysOneSide % MOD);
30    }
31}
32
1class Solution {
2public:
3    int countHousePlacements(int n) {
4        const int MOD = 1e9 + 7;
5      
6        // Dynamic programming arrays
7        // hasHouse[i]: number of ways to place houses up to position i with a house at position i
8        // noHouse[i]: number of ways to place houses up to position i without a house at position i
9        int hasHouse[n], noHouse[n];
10      
11        // Base case: at position 0, we can either place a house or not
12        hasHouse[0] = 1;  // One way to place a house at position 0
13        noHouse[0] = 1;   // One way to not place a house at position 0
14      
15        // Fill the DP arrays for positions 1 to n-1
16        for (int i = 1; i < n; ++i) {
17            // If we place a house at position i, the previous position must be empty
18            hasHouse[i] = noHouse[i - 1];
19          
20            // If we don't place a house at position i, the previous position can be either empty or have a house
21            noHouse[i] = (hasHouse[i - 1] + noHouse[i - 1]) % MOD;
22        }
23      
24        // Total ways for one side of the street
25        long waysOneSide = hasHouse[n - 1] + noHouse[n - 1];
26      
27        // Since we have two sides of the street and they are independent,
28        // the total number of ways is the square of ways for one side
29        return (waysOneSide * waysOneSide) % MOD;
30    }
31};
32
1/**
2 * Counts the number of ways to place houses on both sides of a street
3 * @param n - The number of plots on each side of the street
4 * @returns The total number of valid house placement combinations modulo 10^9 + 7
5 */
6function countHousePlacements(n: number): number {
7    // Arrays to track placement possibilities
8    // placeHouse[i]: number of ways when placing a house at position i
9    // skipHouse[i]: number of ways when not placing a house at position i
10    const placeHouse: bigint[] = new Array(n);
11    const skipHouse: bigint[] = new Array(n);
12  
13    // Base case: one way to place or skip a house at position 0
14    placeHouse[0] = 1n;
15    skipHouse[0] = 1n;
16  
17    // Modulo value for preventing overflow
18    const MOD: bigint = BigInt(10 ** 9 + 7);
19  
20    // Dynamic programming: calculate possibilities for each position
21    for (let i = 1; i < n; ++i) {
22        // Can only place a house at position i if position i-1 was skipped
23        placeHouse[i] = skipHouse[i - 1];
24      
25        // Can skip position i regardless of what happened at position i-1
26        skipHouse[i] = (placeHouse[i - 1] + skipHouse[i - 1]) % MOD;
27    }
28  
29    // Total ways for one side of the street
30    const oneSideWays: bigint = placeHouse[n - 1] + skipHouse[n - 1];
31  
32    // Square the result for both sides of the street (independent placement)
33    return Number(oneSideWays ** 2n % MOD);
34}
35

Time and Space Complexity

The time complexity is O(n), where n is the length of the street. This is because the algorithm uses a single for loop that iterates from index 1 to n-1, performing constant time operations in each iteration. The loop executes n-1 times, and all operations inside the loop (assignments and modulo operations) take O(1) time.

The space complexity is O(n), where n is the length of the street. The algorithm creates two arrays f and g, each of size n, to store the dynamic programming states. These arrays dominate the space usage, requiring 2n space which simplifies to O(n). The additional variables mod and v only use constant space O(1), which doesn't affect the overall space complexity.

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

Common Pitfalls

1. Forgetting to Apply Modulo During Intermediate Calculations

A critical mistake is only applying the modulo operation at the final step when squaring the result. This can cause integer overflow during the dynamic programming calculation phase, especially for large values of n.

Incorrect approach:

# Missing modulo in the addition
dp_no_place[i] = dp_place[i - 1] + dp_no_place[i - 1]  # Can overflow!

# Only applying modulo at the end
one_side_total = (dp_place[-1] + dp_no_place[-1]) % MOD

Correct approach:

# Apply modulo during the DP calculation
dp_no_place[i] = (dp_place[i - 1] + dp_no_place[i - 1]) % MOD

# Also apply modulo when calculating one_side_total
one_side_total = (dp_place[-1] + dp_no_place[-1]) % MOD

2. Misunderstanding the Independence of Street Sides

Some might try to create a complex 2D DP solution tracking both sides simultaneously, leading to unnecessary complexity and potential errors. The key insight is recognizing that the two sides are completely independent.

Overcomplicated approach:

# Unnecessarily tracking both sides together
dp = [[0] * 4 for _ in range(n)]  # 4 states: (empty,empty), (empty,house), etc.

Simplified correct approach:

# Calculate for one side only, then square the result
one_side_total = dp_place[-1] + dp_no_place[-1]
result = (one_side_total * one_side_total) % MOD

3. Space Optimization Oversight

While the current solution uses O(n) space with two arrays, it can be optimized to O(1) space since we only need the previous state to calculate the current state.

Space-optimized solution:

def countHousePlacements(self, n: int) -> int:
    MOD = 10**9 + 7
  
    # Only track previous states
    prev_place = 1
    prev_no_place = 1
  
    for i in range(1, n):
        curr_place = prev_no_place
        curr_no_place = (prev_place + prev_no_place) % MOD
        prev_place = curr_place
        prev_no_place = curr_no_place
  
    one_side_total = (prev_place + prev_no_place) % MOD
    return (one_side_total * one_side_total) % MOD

4. Off-by-One Errors in Base Cases

Another common mistake is incorrectly initializing the base cases or misunderstanding what index represents what position.

Potential confusion:

  • Forgetting that dp_place[0] represents placing a house at the first position (not position 0)
  • Incorrectly setting base cases to 0 instead of 1
  • Using wrong array indices when accessing the final result
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?


Recommended Readings

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

Load More