Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions CoinChange2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Did this code successfully run on Leetcode : Yes
// Any problem you faced while coding this : No
// Approach :

// We divide the problem to find out which combination of coins([C1], [C1, C2], .... [C1, CN]) can add up to given amount.
// So, we can choose a coin, how many ever times it is needed to make an amount, but once it is selected we will not
// select that coin again and will move to find the combination with the next coin. So the number of ways will,
// C = Combinations(starting with C1) + Combinations(starting with C2)..... Combinations(starting with CN)
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CoinChange2 {

public int change() {
int amount = 5;
int[] coins = {1,2,5};

// int numberOfWays = changeRecursive(amount, coins, 0);
// int numberOfWays = changeMemo(amount, coins, 0, new HashMap<>());
// int numberOfWays = changeTabulation(amount, coins);
return changeTabulation1D(amount, coins);
}

public int changeTabulation1D(int amount, int[] coins){
// N: number of values in coins, A: amount
// Time Complexity : O(NA), Will need to go through all amount values for each coin;
// Space Complexity : O(A)
int rows = coins.length+1;
int cols = amount+1;
int[] table = new int[cols];

for (int i = 1; i < rows; i++) {
int currentCoin = coins[i-1];
// indicates to make sum 0 using any coin[i], there is at least 1 way, that is by skipping the coin
table[0] = 1;
for (int j = 1; j < cols; j++) {
if(currentCoin <= j){
//Save the number to make the amount j using current coin and using the previous coin(value already saved at
// at current cell)
table[j] = table[j-currentCoin] + table[j];
}
}
}

return table[cols-1];
}


private int changeTabulation(int amount, int[] coins){
// N: number of values in coins, A: amount
// Time Complexity : O(NA), Will need to go through matrix of size NxA;
// Space Complexity : O(NA)
int rows = coins.length+1;
int cols = amount+1;

int[][] table = new int[rows][cols];
for (int i = 0; i < rows; i++) {
// indicates to make sum 0 using any coin[i], there is at least 1 way, that is by skipping the coin
table[i][0] = 1;
}

// Go through each row, calculating number of ways to create amount j using coin[i]
for(int i = 1; i < rows; i++){
int currentCoin = coins[i-1];
for(int j = 1; j < cols; j++){
int include = 0;
if(currentCoin <= j){
// Give me number of ways to count the rest of value (amount-coin[i]) using current coin itself
include = table[i][j-currentCoin];
}
// dont include the current coin, get number of ways from previous coins to create j amount
int exclude = table[i-1][j];
table[i][j] = include + exclude;
}
}
return table[rows-1][cols-1];
}

private int changeMemo(int amount, int[] coins, int index, HashMap<List<Integer>, Integer> memo) {
// N: number of values in coins, A: amount
// Time Complexity : O(2(N+A)) = ~ O(N+A), fetches already calculated subproblem from memo
// Space Complexity : O(N+A), max depth of recursion calls, so N+A calls on the stack frame.
// Also, similar amount of values in the memo hashmap

if(amount == 0) return 1;
if(amount < 0) return 0;
if(index >= coins.length) return 0;

List<Integer> key = List.of(amount, index);
if(memo.containsKey(key)) return memo.get(key);

int include = changeMemo(amount-coins[index],coins, index, memo);
int exclude = changeMemo(amount, coins, index+1, memo);
int numberOfWays = include + exclude;
memo.put(key, numberOfWays);
return numberOfWays;
}

private int changeRecursive(int amount, int[] coins, int index) {
// N: number of values in coins, A: amount
// Time Complexity : O(2^(N+ A)), at every subproblem call, we make decision, whether to take that coin or skip that coin
// for example, we can take the coin A from 0 to multiple times, till it reaches the amount. So N coins each can be taken till "amount" times
// Space Complexity : O(N+A)
// Since we are not going to use the same coin again, only first case will occur, 2nd and 3rd combination would not.
// 5 : 1+1+1+2...
// 5 : 1+2+1+1
// 5 : 2+1+1+1
if (amount < 0) return 0;
if (amount == 0) return 1;
if (index >= coins.length) return 0;

// take this coin till it makes the amount and never take it again later.
int include = changeRecursive(amount - coins[index], coins, index);

// skip the coin
int exclude = changeRecursive(amount, coins, index+1);
return include + exclude;
}

// Added for personal notes
public int changeRecursiveIncorrect(int amount, int[] coins){
// If we use an approach that we used in Coin change 1, it would cause duplicates like, so incorrect number of ways
// 5 = 1+1+1+2
// 5 = 2+1+1+1
if(amount < 0) return 0;
if(amount == 0) return 1;

int numberOfWays = 0;
for(int coin: coins){
if(coin <= amount){
numberOfWays += changeRecursiveIncorrect(amount-coin, coins);
}
}

return numberOfWays;
}


}
134 changes: 134 additions & 0 deletions PaintHouse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Did this code successfully run on Leetcode : Don't have premium account
// Any problem you faced while coding this : No
// Approach :

// Since we cant have two houses of the same color, we need to track the current color chosen and pass it to the recursive calls. For example, if blue is chosen, we will
// try to find minimum if we chose red or green for the next house. And then add it to the blue paint cost. At any node,in the above manner, we will calculate what is cost
// of painting the house with the three colors and find their minimum value.
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PaintHouse {

static void main(String[] args) {
// int[][] costs = {{17,2,17}, {16, 16, 5}, {14,3,9}};
int[][] costs = {{7, 7, 7}, {6, 6, 6}, {4, 4, 9}};

int withRedPaint = minPaintCost(costs, 0, 0);
int withBluePaint = minPaintCost(costs, 0, 1);
int withGreenPaint = minPaintCost(costs, 0, 2);

int minPaintCost = Math.min(withRedPaint, Math.min(withGreenPaint, withBluePaint));
System.out.println("Paint cost with Recursion: "+ minPaintCost);

int withRedPaintMemo = minPaintCostMemo(costs, 0, 0, new HashMap<>());
int withBluePaintMemo = minPaintCostMemo(costs, 0, 1, new HashMap<>());
int withGreenPaintMemo = minPaintCostMemo(costs, 0, 2, new HashMap<>());
int minPaintCostMemo = Math.min(withRedPaintMemo, Math.min(withBluePaintMemo, withGreenPaintMemo));
System.out.println("Paint cost with Memo: "+ minPaintCostMemo);

int minTabulation = minPaintCostTabulation(costs);
System.out.println("Paint cost with Tabulation: "+minTabulation);

int minSpace = minPaintCostSpaceOptimization(costs);
System.out.println("Paint cost with Tabulation and space optimization: " + minSpace);
}

public static int minPaintCostSpaceOptimization(int[][] costs) {
// Time Complexity : O(N), where N is number of houses
// Space Complexity : O(1), use 3 variables to keep track of what is current min if we choose color Red, Green or Blue
int minRed = 0, minBlue = 0, minGreen = 0;

for (int[] cost : costs) {
int prevRed = minRed;
int prevBlue = minBlue;
int prevGreen = minGreen;

// Calculate value at each house if the current color is chosen, then add it with the minimum cost of painting the previous house with other 2 colors
// as we cant have two adjacent houses painted with same color
minRed = cost[0] + Math.min(prevBlue, prevGreen);
minBlue = cost[1] + Math.min(prevRed, prevGreen);
minGreen = cost[2] + Math.min(prevRed, prevBlue);
}

return Math.min(minRed, Math.min(minBlue, minGreen));
}

public static int minPaintCostTabulation(int[][] costs) {
// Time Complexity : O(NC), where N is number of houses, C is number of colors
// Space Complexity : O(NC), store value at each house by adding the current chosen color with minimum of other two colors for the previous house
int houses = costs.length+1;
int colors = costs[0].length;
int[][] minCost = new int[houses][colors];
// The first row is all 0s, so that house 1 can use this result

for(int i = 1; i < houses; i++){
for (int j = 0; j < colors; j++) {
if(j == 0) {
// current color cost + min of colors for prev house
minCost[i][j] = costs[i-1][0] + Math.min(minCost[i-1][1], minCost[i-1][2]);
}else if(j == 1){
minCost[i][j] = costs[i-1][1] + Math.min(minCost[i-1][0], minCost[i-1][2]);
}else {
minCost[i][j] = costs[i-1][2] + Math.min(minCost[i-1][0], minCost[i-1][1]);
}
}
}
// The last row has all the costs for different colors added up, so find the min of the three.
return Math.min(minCost[houses-1][0], Math.min(minCost[houses-1][1], minCost[houses-1][2]));
}

public static int minPaintCostMemo(int[][] costs, int houseIdx, int currentColor, Map<List<Integer>, Integer> memo) {
// Time Complexity : O(NC), where N is number of houses, C is number of colors
// Space Complexity : O(N), the recursion stack will be deep till it evaluates all houses.

if (houseIdx >= costs.length) return 0;

// Keep the changing house index and color in the memo
List<Integer> key = List.of(houseIdx, currentColor);
if (memo.containsKey(key)) {
return memo.get(key);
}

int currentCost = costs[houseIdx][currentColor];
if (currentColor == 0) {
currentCost += Math.min(minPaintCostMemo(costs, houseIdx + 1, 1, memo), minPaintCostMemo(costs, houseIdx + 1, 2, memo));
}


if (currentColor == 1) {
currentCost += Math.min(minPaintCostMemo(costs, houseIdx + 1, 0, memo), minPaintCostMemo(costs, houseIdx + 1, 2, memo));
}

if (currentColor == 2) {
currentCost += Math.min(minPaintCostMemo(costs, houseIdx + 1, 0, memo), minPaintCostMemo(costs, houseIdx + 1, 1, memo));
}

memo.put(key, currentCost);
return currentCost;
}

public static int minPaintCost(int[][] costs, int houseIdx, int currentColor) {
// Time Complexity : O(3(2^N)). The recursion levels will have 3, 6, 12 calls, till N houses are evaluated
// Space Complexity : O(N), the recursion stack will be deep till it evaluates all N houses.
if (houseIdx >= costs.length) return 0;

int currentCost = costs[houseIdx][currentColor];

// If we take any current color. Also find the min cost if we chose any of other 2 color for the next house recursively.
if (currentColor == 0) {
currentCost += Math.min(minPaintCost(costs, houseIdx + 1, 1), minPaintCost(costs, houseIdx + 1, 2));
}

if (currentColor == 1) {
currentCost += Math.min(minPaintCost(costs, houseIdx + 1, 0), minPaintCost(costs, houseIdx + 1, 2));
}

if (currentColor == 2) {
currentCost += Math.min(minPaintCost(costs, houseIdx + 1, 0), minPaintCost(costs, houseIdx + 1, 1));
}

return currentCost;
}
}