322. Coin Change

322. Coin Change
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1
Example 2:
Input: coins = [2], amount = 3
Output: -1
Note:
You may assume that you have an infinite number of each kind of coin.


https://leetcode.com/problems/coin-change/solution/




Approach #1 

Backtracking  all possibilities 

A trivial solution is to enumerate all subsets of coin frequencies 
 that satisfy the constraints above, compute their sums and return the minimum among them.
Algorithm
To apply this idea, the algorithm uses backtracking technique to generate all combinations of coin frequencies  in the range which satisfy the constraints above. It makes a sum of the combinations and returns their minimum or 
-1 in case there is no acceptable combination.



public class Solution {    

    public int coinChange(int[] coins, int amount) {
        return coinChange(0, coins, amount);
    }

    private int coinChange(int idxCoin, int[] coins, int amount) {
        if (amount == 0)
            return 0;
        if (idxCoin < coins.length && amount > 0) {
            int maxVal = amount/coins[idxCoin];
            int minCost = Integer.MAX_VALUE;
            for (int x = 0; x <= maxVal; x++) {
                if (amount >= x * coins[idxCoin]) {
                    int res = coinChange(idxCoin + 1, coins, amount - x * coins[idxCoin]);
                    if (res != -1)
                        minCost = Math.min(minCost, res + x);
                }                    
            }           
            return (minCost == Integer.MAX_VALUE)? -1: minCost;
        }        
        return -1;
    }  
}

// Time Limit Exceeded













Approach #2 (Dynamic programming - Top down) 
We note that this problem has an optimal substructure property, which is the key piece in solving any Dynamic Programming problems. In other words, the optimal solution can be constructed from optimal solutions of its subproblems. How to split the problem into subproblems? Let's assume that we know 
S which is optimal and the last coin's denomination is  Then the following equation should be true because of optimal substructure of the problem:


In the recursion tree above, we could see that a lot of subproblems were calculated multiple times. For example the problem F(1) was calculated 13 times. Therefore we should cache the solutions to the subproblems in a table and access them in constant time when necessary

Algorithm
The idea of the algorithm is to build the solution of the problem from top to bottom. It applies the idea described above. It use backtracking and cut the partial solutions in the recursive tree, which doesn't lead to a viable solution. Тhis happens when we try to make a change of a coin with a value greater than the amount 
S. To improve time complexity we should store the solutions of the already calculated subproblems in a table.


public class Solution {

    public int coinChange(int[] coins, int amount) {        
        if (amount < 1) return 0;
        return coinChange(coins, amount, new int[amount]);
    }

    private int coinChange(int[] coins, int rem, int[] count) {
        if (rem < 0) return -1;
        if (rem == 0) return 0;
        if (count[rem - 1] != 0) return count[rem - 1];
        int min = Integer.MAX_VALUE;
        for (int coin : coins) {
            int res = coinChange(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];
    }
}


Complexity Analysis
* Time complexity : O(S*n)

O(S∗n). where S is the amount, n is denomination count. In the worst case the recursive tree of the algorithm has height of S and the algorithm solves only S subproblems because it caches precalculated solutions in a table. Each subproblem is computed with n iterations, one by coin denomination. Therefore there is O(S*n) time complexity.

* Space complexity : O(S), where S is the amount to change We use extra space for the memoization table.



Approach #3 (Dynamic programming - Bottom up) [Accepted]
Algorithm
For the iterative solution, we think in bottom-up manner. Before calculating
F(i), we have to compute all minimum counts for amounts up to i


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];
    }
}









Dp
https://leetcode.com/problems/coin-change/discuss/77368/*Java*-Both-iterative-and-recursive-solutions-with-explanations



Uber
 硬币题,给一个target value K和一堆硬币,求最少个数的硬币组合,要把组合输出来。 楼主说了用BFS或者DP。 最后用了BFS做,好写一些



// return any one shortest path of minimum coins 

private List<Integer> res = new ArrayList<>();
private HashMap<Integer, Integer> map = new HashMap<>();
public List<Integer> shortestPath(int[] coins, int amount){
    
    // bfs to find the shortest path 
    // the shortes path might not exist, so use level to see if there is a path 
    // if no such path exist, return empty list 
    // dfs to traverse to print the shortest path 
    if(bfs(coins, amount) != -1){
        return dfs(0, amount);
    }
    return res;
}
private int bfs(int[] coins, int amount){
    // we try differnt coins on every level 
    // if for the first time that the remaining amount is 0, we know we have found the shortest path
    // and we can return 
    // during the process , use map to record the remaining amount(key), and the coin(value)
    int level = 1;
    Queue<Integer> queue = new LinkedList<>();
    map.put(amount, 0);
    queue.offer(amount);
    while(!queue.isEmpty()){
        int size = queue.size();
        for(int i = 0; i < size; i++){
            int remain = queue.poll();
            for(int j = 0; j < coins.length; j++){
                int newRemain = remain - coins[j];
                if(newRemain < 0) break; // based on the assumption that the coins are sorted
                // no need to check the following coins , because its increasing 
                map.put(newRemain, coins[j]);
                if(newRemain == 0) return level;
                queue.offer(newRemain);
            }    
        }
        level++;
        
    }
    return -1;
    
}

private List<Integer> dfs(int init, int goal){
   
    int remainSum = init;
    while(remainSum != goal){
        res.add(map.get(remainSum));
        remainSum += map.get(remainSum);
    }
    return res;
}
    











Bfs 
https://leetcode.com/problems/coin-change/discuss/77361/Fast-Python-BFS-Solution


class Solution {
    public int coinChange(int[] coins, int amount) {
        if(amount == 0) return 0;
        int level = 1;
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(amount);
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i = 0; i < size; i++){
                int cur = queue.poll();
                for(int j = 0; j < coins.length; j++){
                    if(cur < coins[j]) break;
                    int newVal = cur - coins[j];
                    if(newVal == 0) return level;
                    if(newVal > 0) queue.offer(newVal);
                }
            }
            level++;
        }
        return -1;
    }
}


// bfs shortest path and use map to record the path 
 



You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1
Example 2:
Input: coins = [2], amount = 3
Output: -1
Note:
You may assume that you have an infinite number of each kind of coin.



https://www.youtube.com/watch?v=uUETHdijzkA&t=388s

https://www.youtube.com/watch?v=htdBJul3xoc

 

import java.util.Arrays;
import java.util.HashSet;

// A naive recursive Java implementation
// to count number of decodings that
// can be formed from a given digit sequence
public class Solution {

    public static int coinChange(int[] coins, int amount) {

        // We use this to fill the dp table with default values
        int max = amount + 1;

        // This table will store the answer to our sub problems
        int[] dp = new int[amount + 1];

        // Initialize the dp table
        Arrays.fill(dp, max);

    /*
      The answer to making change with minimum coins for 0
      will always be 0 coins no matter what the coins we are
      given are
    */
        dp[0] = 0;

        // Solve every subproblem from 1 to amount
        for (int i = 1; i <= amount; i++) {
            System.out.println("i :" + i);
            // For each coin we are given
            for (int j = 0; j < coins.length; j++) {
                System.out.println("j :" + j);
                // If it is less than or equal to the sub problem amount
                System.out.println("coins[j] :" + coins[j]);
                boolean isSmaller = coins[j] <= i;
                System.out.println("coins[j] <= i :" + isSmaller);

                if (coins[j] <= i) {
                    // Try it. See if it gives us a more optimal solution
                    System.out.println("dp[i] :" + dp[i]);
                    int num = i - coins[j];
                    System.out.println("i - coins[j] :" + num);
                    int dpNum = dp[i - coins[j]];
                    System.out.println("dp[i - coins[j]] :" + dpNum);

                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                    System.out.println("dp[i] :" + dp[i]);
                }
            }
            System.out.println("//////////////");
        }

    /*
      dp[amount] has our answer. If we do not have an answer then dp[amount]
      will be amount + 1 and hence dp[amount] > amount will be true. We then
      return -1.
      Otherwise, dp[amount] holds the answer
    */
        return dp[amount] > amount ? -1 : dp[amount];
    }





    // Driver program to test above function
    public static void main(String[] args)
    {
        int[] coins = new int[]{1, 2, 5};
        int amount = 11;
        System.out.println(coinChange(coins, amount));
    }
}






i :1
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :0
dp[i - coins[j]] :0
dp[i] :1
j :1
coins[j] :2
coins[j] <= i :false
j :2
coins[j] :5
coins[j] <= i :false
//////////////
i :2
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :1
dp[i - coins[j]] :1
dp[i] :2
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :2
i - coins[j] :0
dp[i - coins[j]] :0
dp[i] :1
j :2
coins[j] :5
coins[j] <= i :false
//////////////
i :3
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :2
dp[i - coins[j]] :1
dp[i] :2
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :2
i - coins[j] :1
dp[i - coins[j]] :1
dp[i] :2
j :2
coins[j] :5
coins[j] <= i :false
//////////////
i :4
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :3
dp[i - coins[j]] :2
dp[i] :3
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :3
i - coins[j] :2
dp[i - coins[j]] :1
dp[i] :2
j :2
coins[j] :5
coins[j] <= i :false
//////////////
i :5
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :4
dp[i - coins[j]] :2
dp[i] :3
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :3
i - coins[j] :3
dp[i - coins[j]] :2
dp[i] :3
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :3
i - coins[j] :0
dp[i - coins[j]] :0
dp[i] :1
//////////////
i :6
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :5
dp[i - coins[j]] :1
dp[i] :2
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :2
i - coins[j] :4
dp[i - coins[j]] :2
dp[i] :2
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :2
i - coins[j] :1
dp[i - coins[j]] :1
dp[i] :2
//////////////
i :7
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :6
dp[i - coins[j]] :2
dp[i] :3
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :3
i - coins[j] :5
dp[i - coins[j]] :1
dp[i] :2
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :2
i - coins[j] :2
dp[i - coins[j]] :1
dp[i] :2
//////////////
i :8
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :7
dp[i - coins[j]] :2
dp[i] :3
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :3
i - coins[j] :6
dp[i - coins[j]] :2
dp[i] :3
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :3
i - coins[j] :3
dp[i - coins[j]] :2
dp[i] :3
//////////////
i :9
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :8
dp[i - coins[j]] :3
dp[i] :4
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :4
i - coins[j] :7
dp[i - coins[j]] :2
dp[i] :3
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :3
i - coins[j] :4
dp[i - coins[j]] :2
dp[i] :3
//////////////
i :10
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :9
dp[i - coins[j]] :3
dp[i] :4
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :4
i - coins[j] :8
dp[i - coins[j]] :3
dp[i] :4
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :4
i - coins[j] :5
dp[i - coins[j]] :1
dp[i] :2
//////////////
i :11
j :0
coins[j] :1
coins[j] <= i :true
dp[i] :12
i - coins[j] :10
dp[i - coins[j]] :2
dp[i] :3
j :1
coins[j] :2
coins[j] <= i :true
dp[i] :3
i - coins[j] :9
dp[i - coins[j]] :3
dp[i] :3
j :2
coins[j] :5
coins[j] <= i :true
dp[i] :3
i - coins[j] :6
dp[i - coins[j]] :2
dp[i] :3
//////////////
3

 

posted on 2018-11-06 07:59  猪猪&#128055;  阅读(273)  评论(0)    收藏  举报

导航