From 3d9d6e07feadac47aaa8b0c81f810c5f0ffad2eb Mon Sep 17 00:00:00 2001 From: Mayank Padshala Date: Sat, 29 Nov 2025 22:40:39 +0000 Subject: [PATCH] Complete DP-2 --- coin_change_two.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++ paint_house.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 coin_change_two.py create mode 100644 paint_house.py diff --git a/coin_change_two.py b/coin_change_two.py new file mode 100644 index 00000000..2174b603 --- /dev/null +++ b/coin_change_two.py @@ -0,0 +1,48 @@ +class Solution: + """ + Time Complexity: O(m * n) + m = number of coins, n = amount + We fill an m x n DP table. + + Space Complexity: O(m * n) + Using a 2D DP array dp[m+1][n+1]. + + Approaches: + 1. Recursion + Memoization: + Try using or skipping each coin and recursively compute ways + to reach the remaining amount. Cache results to avoid recomputation. + + 2. Bottom-up DP (2D Tabulation): + Create a DP table where dp[i][j] represents the number of ways + to make amount j using the first i coins. + For each coin, we decide: + - Not using the coin (dp[i-1][j]) + - Using the coin (dp[i][j - coin value]) + + 3. Bottom-up DP (Optimized to 1D array): + Iterate coins first, then amount—which ensures combinations + (no permutation counting) and reduces space to O(n). + """ + def change(self, amount: int, coins: List[int]) -> int: + m = len(coins) + n = amount + + # dp[i][j] = number of ways to form amount j using first i coins + dp = [[0] * (n + 1) for _ in range(m + 1)] + + # Base case: One way to form amount 0 (choose nothing) + dp[0][0] = 1 + + for i in range(1, m + 1): + for j in range(n + 1): + # If current amount is less than the coin value, + # we cannot use this coin + if j < coins[i - 1]: + dp[i][j] = dp[i - 1][j] + else: + # Two choices: + # 1. Don't use the coin -> dp[i - 1][j] + # 2. Use the coin -> dp[i][j - coin_value] + dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]] + + return dp[m][n] diff --git a/paint_house.py b/paint_house.py new file mode 100644 index 00000000..43f410c6 --- /dev/null +++ b/paint_house.py @@ -0,0 +1,45 @@ +class Solution: + """ + Time Complexity: O(n) -> We process each house once. + Space Complexity: O(1) -> We only store costs for 3 colors per step. + + Approaches: + 1. Top-down DP (Recursion + Memoization): + Recursively explore all ways to paint each house while ensuring no two adjacent + houses share the same color. Cache results of subproblems to avoid recomputation. + + 2. Bottom-up DP (Tabulation with DP Table): + Build a dp[n][3] table where each row stores the minimum cost to paint up to + that house with each color. Each entry depends on the minimum of the other two + colors from the previous house. + + 3. Bottom-up DP with Space Optimization (Tabulation -> O(1) Space): + Instead of a full dp table, keep only the previous house's three minimum costs. + Update these values iteratively to compute the final minimum cost. + """ + def minCost(self, costs: List[List[int]]) -> int: + n = len(costs) + + # If there are no houses, total cost is 0 + if n == 0: + return 0 + + # Initialize minimum costs for painting the first house + chooseRedPrev = costs[0][0] + chooseBluePrev = costs[0][1] + chooseGreenPrev = costs[0][2] + + # Iterate through each subsequent house + for i in range(1, n): + # Compute the new costs if we paint this house each color + chooseRed = costs[i][0] + min(chooseBluePrev, chooseGreenPrev) + chooseBlue = costs[i][1] + min(chooseRedPrev, chooseGreenPrev) + chooseGreen = costs[i][2] + min(chooseRedPrev, chooseBluePrev) + + # Update previous values for the next iteration + chooseRedPrev, chooseBluePrev, chooseGreenPrev = ( + chooseRed, chooseBlue, chooseGreen + ) + + # The answer is the minimum of the last computed color choices + return min(chooseRedPrev, chooseBluePrev, chooseGreenPrev)