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 猪猪🐷 阅读(273) 评论(0) 收藏 举报
浙公网安备 33010602011771号