From b78aadffd0eda00ca2d96e04255d8bfcef46933e Mon Sep 17 00:00:00 2001 From: Harshitha Thondalapally Date: Mon, 5 Jan 2026 15:49:35 -0500 Subject: [PATCH] Completed DP 2 problems --- src/CoinChangeII.java | 100 ++++++++++++++++++++++++++++++++++ src/PaintHouse.java | 124 ++++++++++++++++++++++++++++++++++++++++++ src/TestDP.java | 52 ++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 src/CoinChangeII.java create mode 100644 src/PaintHouse.java create mode 100644 src/TestDP.java diff --git a/src/CoinChangeII.java b/src/CoinChangeII.java new file mode 100644 index 00000000..d8ceb5c8 --- /dev/null +++ b/src/CoinChangeII.java @@ -0,0 +1,100 @@ +import java.util.Arrays; + +/* +COIN CHANGE II (LeetCode 518) + +We solve this problem using multiple approaches: + +1) Pure Recursion (Exponential) +2) Top-Down DP (Recursion + Memoization) +3) Bottom-Up DP (2D Matrix) +4) Bottom-Up DP (1D Optimized) + +Key Difference from Coin Change I: +- Coin Change I → MIN coins +- Coin Change II → COUNT number of ways + +State idea: +dp[i][j] = number of ways to make amount j using first i coins +*/ + +public class CoinChangeII { + /*1) PURE RECURSION */ + public int changeRecursive(int amount, int[] coins) { + return helperRecursive(amount, coins, 0); + } + + private int helperRecursive(int amount, int[] coins, int index) { + if (amount == 0) return 1; // found one valid way + if (amount < 0 || index == coins.length) return 0; + + // choice: skip coin OR take coin + int skip = helperRecursive(amount, coins, index + 1); + int take = helperRecursive(amount - coins[index], coins, index); + + return skip + take; + } + + /* 2) TOP-DOWN DP (Memoization) */ + private int[][] memo; + + public int changeTopDown(int amount, int[] coins) { + memo = new int[coins.length][amount + 1]; + for (int i = 0; i < coins.length; i++) { + Arrays.fill(memo[i], -1); + } + return helperTopDown(amount, coins, 0); + } + + private int helperTopDown(int amount, int[] coins, int index) { + if (amount == 0) return 1; + if (amount < 0 || index == coins.length) return 0; + + if (memo[index][amount] != -1) { + return memo[index][amount]; + } + + int skip = helperTopDown(amount, coins, index + 1); + int take = helperTopDown(amount - coins[index], coins, index); + + memo[index][amount] = skip + take; + return memo[index][amount]; + } + + /*3) BOTTOM-UP DP (2D Matrix)*/ + public int change2D(int amount, int[] coins) { + int n = coins.length; + int[][] dp = new int[n + 1][amount + 1]; + + // Base case: 1 way to make amount 0 (choose nothing) + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= amount; j++) { + if (j < coins[i - 1]) { + dp[i][j] = dp[i - 1][j]; // can't take coin + } else { + dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]]; + } + } + } + + return dp[n][amount]; + } + + /* 4) BOTTOM-UP DP (1D Optimized) */ + public int change1D(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + dp[0] = 1; // one way to make amount 0 + + for (int coin : coins) { + for (int j = coin; j <= amount; j++) { + dp[j] += dp[j - coin]; + } + } + + return dp[amount]; + } +} diff --git a/src/PaintHouse.java b/src/PaintHouse.java new file mode 100644 index 00000000..1989d925 --- /dev/null +++ b/src/PaintHouse.java @@ -0,0 +1,124 @@ +import java.util.Arrays; + +/* +PAINT HOUSE (LeetCode 256) + +Goal: Min cost to paint all houses such that no two adjacent houses have the same color. +costs[i][c] = cost to paint house i with color c (c in {0,1,2}). + +Approaches: +1) Pure Recursion (exponential) +2) Top-Down DP (Memoization) +3) Bottom-Up DP (2D) +4) Bottom-Up DP (O(1) space) + +Time: +- Recursion: exponential +- DP: O(n) +Space: +- Memo/2D: O(n) +- O(1) space: constant +*/ + +public class PaintHouse { + + /* 1) PURE RECURSION*/ + public int minCostRecursive(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + return Math.min( + helperRec(costs, 0, 0), + Math.min(helperRec(costs, 0, 1), helperRec(costs, 0, 2)) + ); + } + + private int helperRec(int[][] costs, int i, int color) { + int n = costs.length; + if (i == n) return 0; + + int costHere = costs[i][color]; + if (i == n - 1) return costHere; + + int nextMin; + if (color == 0) nextMin = Math.min(helperRec(costs, i + 1, 1), helperRec(costs, i + 1, 2)); + else if (color == 1) nextMin = Math.min(helperRec(costs, i + 1, 0), helperRec(costs, i + 1, 2)); + else nextMin = Math.min(helperRec(costs, i + 1, 0), helperRec(costs, i + 1, 1)); + + return costHere + nextMin; + } + + /* 2) TOP-DOWN DP (MEMO) */ + private int[][] memo; + + public int minCostTopDown(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + int n = costs.length; + memo = new int[n][3]; + for (int i = 0; i < n; i++) Arrays.fill(memo[i], -1); + + return Math.min( + helperMemo(costs, 0, 0), + Math.min(helperMemo(costs, 0, 1), helperMemo(costs, 0, 2)) + ); + } + + private int helperMemo(int[][] costs, int i, int color) { + int n = costs.length; + if (i == n) return 0; + if (memo[i][color] != -1) return memo[i][color]; + + int costHere = costs[i][color]; + int bestNext; + + if (i == n - 1) { + memo[i][color] = costHere; + return memo[i][color]; + } + + if (color == 0) bestNext = Math.min(helperMemo(costs, i + 1, 1), helperMemo(costs, i + 1, 2)); + else if (color == 1) bestNext = Math.min(helperMemo(costs, i + 1, 0), helperMemo(costs, i + 1, 2)); + else bestNext = Math.min(helperMemo(costs, i + 1, 0), helperMemo(costs, i + 1, 1)); + + memo[i][color] = costHere + bestNext; + return memo[i][color]; + } + + /* 3) BOTTOM-UP DP (2D) */ + public int minCost2D(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + int n = costs.length; + + int[][] dp = new int[n][3]; + dp[0][0] = costs[0][0]; + dp[0][1] = costs[0][1]; + dp[0][2] = costs[0][2]; + + for (int i = 1; i < n; i++) { + dp[i][0] = costs[i][0] + Math.min(dp[i - 1][1], dp[i - 1][2]); + dp[i][1] = costs[i][1] + Math.min(dp[i - 1][0], dp[i - 1][2]); + dp[i][2] = costs[i][2] + Math.min(dp[i - 1][0], dp[i - 1][1]); + } + + return Math.min(dp[n - 1][0], Math.min(dp[n - 1][1], dp[n - 1][2])); + } + + /* 4) BOTTOM-UP DP (O(1)) */ + public int minCostOptimized(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + int n = costs.length; + + int r = costs[0][0]; + int g = costs[0][1]; + int b = costs[0][2]; + + for (int i = 1; i < n; i++) { + int newR = costs[i][0] + Math.min(g, b); + int newG = costs[i][1] + Math.min(r, b); + int newB = costs[i][2] + Math.min(r, g); + r = newR; + g = newG; + b = newB; + } + + return Math.min(r, Math.min(g, b)); + } +} diff --git a/src/TestDP.java b/src/TestDP.java new file mode 100644 index 00000000..5e92ee75 --- /dev/null +++ b/src/TestDP.java @@ -0,0 +1,52 @@ +public class TestDP { + public static void main(String[] args) { + // Coin Change II tests + CoinChangeII cc2 = new CoinChangeII(); + + int[] coins1 = {1, 2, 5}; + int amount1 = 5; // ways = 4 + int exp1 = 4; + + int[] coins2 = {2}; + int amount2 = 3; // ways = 0 + int exp2 = 0; + + int[] coins3 = {10}; + int amount3 = 0; // ways = 1 (choose nothing) + int exp3 = 1; + + System.out.println(cc2.changeRecursive(amount1, coins1) == exp1); + System.out.println(cc2.changeTopDown(amount1, coins1) == exp1); + System.out.println(cc2.change2D(amount1, coins1) == exp1); + System.out.println(cc2.change1D(amount1, coins1) == exp1); + + System.out.println(cc2.change1D(amount2, coins2) == exp2); + System.out.println(cc2.change1D(amount3, coins3) == exp3); + + //Paint House tests + PaintHouse ph = new PaintHouse(); + + int[][] costs1 = { + {17, 2, 17}, + {16, 16, 5}, + {14, 3, 19} + }; // expected = 10 + int expPH1 = 10; + + int[][] costs2 = { + {7, 6, 2} + }; // expected = 2 + int expPH2 = 2; + + int[][] costs3 = {}; // expected = 0 + int expPH3 = 0; + + System.out.println(ph.minCostRecursive(costs1) == expPH1); + System.out.println(ph.minCostTopDown(costs1) == expPH1); + System.out.println(ph.minCost2D(costs1) == expPH1); + System.out.println(ph.minCostOptimized(costs1) == expPH1); + + System.out.println(ph.minCostOptimized(costs2) == expPH2); + System.out.println(ph.minCostOptimized(costs3) == expPH3); + } +}