Paint House III

Updated on 11 July, 2025
Paint House III header image

Problem Statement

You are tasked with a challenging optimization scenario in a small city, involving a row of m houses. Each house should be painted with one of n possible colors, defined by labels from 1 to n. Not all houses need repainting, as some houses retain their colors from the last summer. The concept of a "neighborhood" refers to a sequence of adjacent houses painted the same color.

Consider a given city where the following data structures define the painting scheme:

  • houses: an array where houses[i] represents the color of house i. If houses[i] is 0, it means the house is unpainted.
  • cost: a m x n matrix where cost[i][j] represents the cost to paint house i with color j+1.

The goal is to determine the minimal painting cost to achieve exactly target neighborhoods. If the desired number of neighborhoods can't be achieved under the given constraints, the function should return -1.

Examples

Example 1

Input:

houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3

Output:

9

Explanation:

Paint houses of this way [1,2,2,1,1]
This array contains target = 3 neighborhoods, [{1}, {2,2}, {1,1}].
Cost of paint all houses (1 + 1 + 1 + 1 + 5) = 9.

Example 2

Input:

houses = [0,2,1,2,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3

Output:

11

Explanation:

Some houses are already painted, Paint the houses of this way [2,2,1,2,2]
This array contains target = 3 neighborhoods, [{2,2}, {1}, {2,2}].
Cost of paint the first and last house (10 + 1) = 11.

Example 3

Input:

houses = [3,1,2,3], cost = [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], m = 4, n = 3, target = 3

Output:

-1

Explanation:

Houses are already painted with a total of 4 neighborhoods [{3},{1},{2},{3}] different of target = 3.

Constraints

  • m == houses.length == cost.length
  • n == cost[i].length
  • 1 <= m <= 100
  • 1 <= n <= 20
  • 1 <= target <= m
  • 0 <= houses[i] <= n
  • 1 <= cost[i][j] <= 104

Approach and Intuition

To solve the problem of painting houses such that there are exactly target neighborhoods with minimal cost, here's a structured approach:

  1. Understand the constraints:

    • Each house has to be considered for each possible color.
    • If a house is already painted, ignore its cost and respect the existing color.
    • We need to efficiently track changes in neighborhoods as we opt for different colors.
  2. Calculate potential neighborhoods:

    • Start with an array dp[i][j][k], representing the minimal cost when considering the first i houses, ending with color j, having k neighborhoods.
    • Initialize with large numbers for impossibility and zero where applicable.
  3. Dynamic programming to fill up the dp table:

    • For houses that need painting, calculate the cost for every possible color.
    • For houses already painted, transfer and update the cost directly without altering the color.
    • The change of neighborhood is considered when the color changes from the previous house.
  4. Determine transitions:

    • Update costs combining previously computed scenarios (previous house/neighborhoods) considering current house color decisions.
    • Use nested loops effectively to cover scenarios of changing or maintaining neighborhoods.
  5. Optimize neighborhood tracking:

    • Ensure the transition does not exceed the target neighborhoods.
    • Efficient check conditions to minimize recomputation.
  6. Extract the minimum cost:

    • After filling up the dp table, the least value at positions where the color scenarios end satisfying the target neighborhoods provides the answer.
  7. Edge case handling:

    • If no valid configuration can be achieved, return -1.
    • This will be deduced when after computing all possibilities the minimal cost still indicates impossibility (remains at initialized high).

This method capitalizes on the dynamic programming paradigm by building upon previously computed values, ensuring the efficient and effective calculation of the minimal painting cost to achieve the desired neighborhood structure.

Solutions

  • C++
cpp
class Solution {
public:
    const int INF = 1000001;  // upper bound on cost
    
    int minimumCost(vector<int>& homes, vector<vector<int>>& paintCost, int numHomes, int numColors, int targetNeighborhoods) {
        vector<vector<int>> costMemo(targetNeighborhoods + 1, vector<int>(numColors, INF));
    
        for (int col = 1; col <= numColors; col++) {
            if (homes[0] == col) {
                costMemo[1][col - 1] = 0;
            } else if (homes[0] == 0) {
                costMemo[1][col - 1] = paintCost[0][col - 1];
            }
        }
    
        for (int homeIndex = 1; homeIndex < numHomes; homeIndex++) {
            vector<vector<int>> newMemo(targetNeighborhoods + 1, vector<int>(numColors, INF));
    
            for (int neigh = 1; neigh <= min(targetNeighborhoods, homeIndex + 1); neigh++) {
                for (int col = 1; col <= numColors; col++) {
                    if (homes[homeIndex] && col != homes[homeIndex]) continue;
    
                    int minAddedCost = INF;
                    for (int lastCol = 1; lastCol <= numColors; lastCol++) {
                        if (lastCol != col) {
                            minAddedCost = min(minAddedCost, costMemo[neigh - 1][lastCol - 1]);
                        } else {
                            minAddedCost = min(minAddedCost, costMemo[neigh][col - 1]);
                        }
                    }
    
                    int additionalCost = homes[homeIndex] ? 0 : paintCost[homeIndex][col - 1];
                    newMemo[neigh][col - 1] = minAddedCost + additionalCost;
                }
            }
            costMemo = newMemo;
        }
    
        int bestCost = INF;
        for (int col = 1; col <= numColors; col++) {
            bestCost = min(bestCost, costMemo[targetNeighborhoods][col - 1]);
        }
    
        return bestCost == INF ? -1 : bestCost;
    }
};

The given C++ program addresses the problem of determining the minimum cost to paint all the houses such that a certain number of neighborhoods are formed, each neighborhood having houses of the same color. The solution provided within the Solution class uses dynamic programming to efficiently handle the problem constraints.

This approach initializes a 2D dynamic programming table, costMemo, where costMemo[i][j] stores the minimum cost to form i neighborhoods considering the first j colors. Key steps of the solution include:

  1. Initialization of the base case for the first house based on whether it is already painted or not.
  2. Iteration through each house and each possible neighborhood count, updating the DP table for each color based on the cost of painting the current house and transitioning from different neighborhoods.
  3. Minimizing the added costs by considering whether changing colors would lead to forming a new neighborhood or remaining in the current one.
  4. Updating the color costs by considering both the changing of the neighborhood or continuing in the same neighborhood while ensuring that already painted houses maintain their color, and unpainted houses are evaluated for every possible color option.
  5. At the end, the algorithm finds the minimum cost required to achieve the exact number of neighborhoods asked.

The time complexity of the solution is controlled by nested loops over homes, neighborhoods, and colors leading to significant processing especially with larger inputs, but remains efficient for the problem constraints. If the final computed minimum cost remains equal to INF, indicating no valid configuration exists, the function returns -1. This scenario is managed by a condition check on the resultant bestCost.

This dynamic programming approach ensures that all scenarios are considered - maintaining or switching neighborhoods based on painting costs and the current state of each house, yielding an optimal solution to the problem.

  • Java
java
class Solution {
    // Upper limit of the cost for safety in calculations
    final int INFINITY_COST = 1000001;
        
    public int calculateMinCost(int[] homes, int[][] paintingCost, int M, int N, int T) {
        int[][] previousRow = new int[T + 1][N];
          
        // Setup initial values for dynamic programming
        for (int t = 0; t <= T; t++) {
            Arrays.fill(previousRow[t], INFINITY_COST);
        }
                
        // Setup base case with the first home
        for (int c = 1; c <= N; c++) {
            if (homes[0] == c) {
                // No cost if already painted
                previousRow[1][c - 1] = 0;
            } else if (homes[0] == 0) {
                // Otherwise, add the painting cost
                previousRow[1][c - 1] = paintingCost[0][c - 1];
            }
        }
            
        for (int i = 1; i < M; i++) {
            int[][] currentRow = new int[T + 1][N];
          
            // Reset currentRow for this iteration
            for (int t = 0; t <= T; t++) {
                Arrays.fill(currentRow[t], INFINITY_COST);
            }
                
            for (int neighborhoods = 1; neighborhoods <= Math.min(T, i + 1); neighborhoods++) {
                for (int newColor = 1; newColor <= N; newColor++) {
                        
                    // Skip if the current home is painted a different color already
                    if (homes[i] != 0 && newColor != homes[i]) continue;
                        
                    int minCost = INFINITY_COST;
                    // Calculate minimum costs based on previous house colors
                    for (int prevColor = 1; prevColor <= N; prevColor++) {
                        if (prevColor != newColor) {
                            minCost = Math.min(minCost, previousRow[neighborhoods - 1][prevColor - 1]);
                        } else {
                            minCost = Math.min(minCost, previousRow[neighborhoods][newColor - 1]);
                        }
                    }
    
                    // Add the painting cost only if not pre-painted
                    int paintingCostHere = homes[i] != 0 ? 0 : paintingCost[i][newColor - 1];
                    currentRow[neighborhoods][newColor - 1] = minCost + paintingCostHere;
                }
            }
            previousRow = currentRow;
        }
            
        int optimalCost = INFINITY_COST;
        // Check for the minimum cost to achieve the desired neighborhoods count
        for (int c = 1; c <= N; c++) {
            optimalCost = Math.min(optimalCost, previousRow[T][c - 1]);
        }
            
        return optimalCost == INFINITY_COST ? -1 : optimalCost;
    }
}

In the Java-based solution for the "Paint House III" problem, the goal is to determine the minimum cost to paint all houses in a row with certain constraints, ensuring that exactly T neighborhoods are painted with at most N different colors.

Here's how the solution efficiently tackles the problem:

  1. Define the method calculateMinCost which takes arrays representing the state of houses (homes), the cost associated with painting each house (paintingCost), and integers representing the number of houses (M), potential colors (N), and neighborhoods (T).

  2. Use dynamic programming by initializing a matrix previousRow to represent the cost of painting up to the current house with a certain number of neighborhoods and last color used. Initialize this array with a high constant, INFINITY_COST, which acts as a de facto infinite value indicating an unachievable state.

  3. Set the base case for the first house by iterating over all potential colors. If a house is already painted that color, the cost is zero; otherwise, it's the cost of painting that house that color.

  4. Loop through each subsequent house (M houses in total) and for each house, calculate the cost scenario for each possible color and neighborhood combination by comparing the cost of maintaining the same neighborhood (same color as the previous house) or forming a new neighborhood (different color from the previous house). Apply minimum cost computations for transitions between previous neighbors.

  5. Use nested loops iterating through desired neighborhoods and possible new colors. Include checks for current painting status and handle already painted houses.

  6. Within these nested loops, compute the minimum costs by comparing the cost for a previous color or creating a new neighborhood. The minimum cost is then updated based on whether the house is already painted.

  7. Transition dynamic programming states by assigning currentRow to previousRow after each house is processed, allowing the move to the next house.

  8. Once all houses are considered, find the optimal solution by determining the minimum value in the final row of the dynamic programming table that corresponds to the desired number of neighborhoods. If no valid solution is found (cost remains INFINITY_COST), return -1.

  9. Return the minimum cost found or -1 indicating no valid configuration meets the problem requirements.

This method ensures handling of edge cases such as pre-painted houses and dynamically managing scenarios where expanding or retaining the number of neighborhoods is necessary. The combination of forward looking dynamic programming with backtracking elements for optimal neighborhood formation and painting decisions provides a solid and optimized approach to this complex problem. The code avoids redundancy and optimizes execution time by iteratively updating an array representing the state-to-state transitions based on painting decisions and existing conditions.

Comments

No comments yet.