题目
思路
- 零钱兑换属于01背包类问题,01背包问题:当前考虑的物品拿或者不拿
- 完全背包问题:当前考虑的物品拿或者不拿,如果拿,只要背包能装下,就可以一直拿,直到背包装不下为止。
- 思路一:暴力递归,每次递归时由两个分支选择这个硬币还是不选择这个硬币,当前硬币面值小于才选择该硬币,最后筛选所有硬币所需个数最小的那种
- 思路二:备忘录,使用容器记录重复递归问题
思路三:动态规划,定义dp[i]表示当前待兑换金额i所需的最小硬币个数,dp[i]依赖于dp[i-coin] + 1和dp[i]的最小和,意思就是待兑换金额i,当使用硬币coin时,此时硬币数加1,剩余待兑换金额为i-coin,如果dp[i-coin]是最小的个数,自然dp[i]也是最小的
代码
//1.暴力递归超时public int coinChange(int[] coins, int amount) {if (amount < 0) return -1;if (amount == 0) return 0;int count = Integer.MAX_VALUE;for (int coin : coins) {int res = coinChange(coins, amount - coin);if (res == -1) continue;count = Math.min(res + 1, count);}return count == Integer.MAX_VALUE ? -1 : count;}//2. 备忘录int[] dp;public int coinChange(int[] coins, int amount) {if (amount < 0) return -1;if (amount == 0) return 0;dp = new int[amount + 1];return recur(coins, amount);}public int recur(int[] coins, int amount) {if(amount < 0) return -1;if(amount == 0) return 0;if(dp[amount] != 0) return dp[amount];int res = Integer.MAX_VALUE;for(int coin : coins) {int r = recur(coins, amount - coin);if(r == -1) continue;res = Math.min(res, r + 1);}dp[amount] = res == Integer.MAX_VALUE ? -1 : res;return dp[amount];}//3. 动态规划,由于使用的dp[i-coin]不固定,所以无法优化dp数组public int coinChange(int[] coins, int amount) {if (amount < 0) return -1;if (amount == 0) return 0;//dp[i]表示amoutn所需的最少硬币个数int[] dp = new int[amount + 1];//初始化一个较大的值即可for(int i = 1; i <= amount; i++) {dp[i] = amount + 1;}for (int i = 1; i <= amount ; i++) {for (int coin : coins) {if (i - coin >= 0)dp[i] = Math.min(dp[i - coin] + 1, dp[i]);}}return dp[amount] == amount + 1 ? -1 : dp[amount];}
