2-7-2018 Change Problem
change problem means find the minimum number of coins needed to make change.
Coin change
This is a very classic dynamic programming algorithm. However, for someone not familiar with the concept, it can be tricky. Here we tackle the problem recursively, for each coin, if I take that coin into account, then the fewest number of coins we can get is 1+coinChange(amount-that_coin_value). So for all the coins, we return the smallest number as min(1+coinChange(amount-coin1_value), 1+coinChange(amount-coin2_value, …).
As we can see it is recursive, the solution is as below, this solution of upper time complexity O(c^n) where c is number of different denominations and n is the amount given, which is exponential:
state: F(S): minimum number of coins needed to make change for amount S using coin denominations[c0, ...cn - 1];
transit function: F(S) = F(S - C) + 1. we compute each F(S - Ci) for each possible denomination c0,c1,c2,...cn-1. And choose minimum among them.
In the recursion tree above, we could see that a lot of subproblems were calculated multiple times. Therefore we should cache the solutions to the subproblems in a table and access them in constant time when necessary.
class Solution {
public int coinChange(int[] coins, int amount) {
if (coins == null || coins.length == 0) return 0;
if (amount < 1) return 0;
return change(coins, amount, new int[amount]);
}
// rem: remaining coins after the last step.
// count[rem] :
private int change(int[] coins, int rem, int[] count) {
// base case
if (rem < 0) return -1;
if (rem == 0) return 0; // pay attention: should return 0.
if (count[rem - 1] != 0) return count[rem - 1]; // already computed, so reuse.
// recursive case
int min = Integer.MAX_VALUE;
for (int coin : coins) {
int res = change(coins, rem - coin, count);
if (res >= 0 && res < min) {
min = 1 + res;
}
}
count[rem - 1] = (min == Integer.MAX_VALUE) ? -1 : min;
return count[rem - 1];
}
}
Time complexity is O(S*n) Space complexity is O(S)
Or we can use Dynamic Programming bottom-up approach.
Before calculating F(i), we have to compute all minimum counts for amounts up to i, On each iteration i of the algorithm F(i) is computed as min F(i - cj) + 1.
F(3)=min{F(3−c1),F(3−c2),F(3−c3)}+1
=min{F(3−1),F(3−2),F(3−3)}+1
=min{F(2),F(1),F(0)}+1
=min{1,1,0}+1
=1
public class Solution {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}