package com.interview.dynamic;
import java.text.Format;
import java.util.HashMap;
import java.util.Map;
/**
* Date 08/12/2013
* @author Tushar Roy
*
* Given a total and coins of certain denomination with infinite supply, what is the minimum number
* of coins it takes to form this total.
*
* Time complexity - O(coins.size * total)
* Space complexity - O(coins.size * total)
*
* Youtube video -
* Topdown DP - https://youtu.be/Kf_M7RdHr1M
* Bottom up DP - https://youtu.be/Y0ZqKpToTic
*/
public class CoinChangingMinimumCoin {
/**
* Top down dynamic programing. Using map to store intermediate results.
* Returns Integer.MAX_VALUE if total cannot be formed with given coins
*/
public int minimumCoinTopDown(int total, int coins[], Map<Integer, Integer> map) {
//if total is 0 then there is nothing to do. return 0.
if ( total == 0 ) {
return 0;
}
//if map contains the result means we calculated it before. Lets return that value.
if ( map.containsKey(total) ) {
return map.get(total);
}
//iterate through all coins and see which one gives best result.
int min = Integer.MAX_VALUE;
for ( int i=0; i < coins.length; i++ ) {
//if value of coin is greater than total we are looking for just continue.
if( coins[i] > total ) {
continue;
}
//recurse with total - coins[i] as new total
int val = minimumCoinTopDown(total - coins[i], coins, map);
//if val we get from picking coins[i] as first coin for current total is less
// than value found so far make it minimum.
if( val < min ) {
min = val;
}
}
//if min is MAX_VAL dont change it. Just result it as is. Otherwise add 1 to it.
min = (min == Integer.MAX_VALUE ? min : min + 1);
//memoize the minimum for current total.
map.put(total, min);
return min;
}
/**
* Bottom up way of solving this problem.
* Keep input sorted. Otherwise temp[j-arr[i]) + 1 can become Integer.Max_value + 1 which
* can be very low negative number
* Returns Integer.MAX_VALUE - 1 if solution is not possible.
*/
public int minimumCoinBottomUp(int total, int coins[]){
int T[] = new int[total + 1];
int R[] = new int[total + 1];
T[0] = 0;
for(int i=1; i <= total; i++){
T[i] = Integer.MAX_VALUE-1;
R[i] = -1;
}
for(int j=0; j < coins.length; j++){
for(int i=1; i <= total; i++){
if(i >= coins[j]){
if (T[i - coins[j]] + 1 < T[i]) {
T[i] = 1 + T[i - coins[j]];
R[i] = j;
}
}
}
}
printCoinCombination(R, coins);
return T[total];
}
private void printCoinCombination(int R[], int coins[]) {
if (R[R.length - 1] == -1) {
System.out.print("No solution is possible");
return;
}
int start = R.length - 1;
System.out.print("Coins used to form total ");
while ( start != 0 ) {
int j = R[start];
System.out.print(coins[j] + " ");
start = start - coins[j];
}
System.out.print("\n");
}
public static void main ( String args[] ) {
int total = 13;
int coins[] = {7, 3, 2, 6};
CoinChangingMinimumCoin cc = new CoinChangingMinimumCoin();
Map<Integer, Integer> map = new HashMap<>();
int topDownValue = cc.minimumCoinTopDown(total, coins, map);
int bottomUpValue = cc.minimumCoinBottomUp(total, coins);
System.out.print(String.format("Bottom up and top down result %s %s", bottomUpValue, topDownValue));
}
}