凑零钱问题
https://leetcode.cn/problems/coin-change/
动规思路:
- 明确最基本的情况:amount==0或者amount<0时得结果都是显而易见的
- 明确状态:即原问题和子问题中会发生变化的量,此处amount显然是我们要找的状态
- 明确选择:即什么样的行为会导致状态发生变化?此处正是我们去选择不同面值的硬币会导致状态发生变化
- 明确dp函数的定义,该函数一定是要包含状态的,否则没法递归,其他变量视情况而定
- 暴力穷举时间复杂度不过关,考虑备忘录做剪枝
-
法一:自顶向下(如面值数组是:[1,2,5])
可以简单画一个递归树,可以发现,最后一个硬币面值只能是1,2,5定下最后一个硬币之后,原问题就可以退化为子问题.
class Solution { int memo[]; public int coinChange(int[] coins, int amount) { memo=new int[amount+1]; Arrays.fill(memo,-2); return dp(coins,amount); } private int dp(int[] coins,int amount){ if(amount==0) return 0; if(amount<0) return -1; if(memo[amount]!=-2) return memo[amount]; int res=Integer.MAX_VALUE; for(int coin : coins){ int subProblem=dp(coins,amount-coin); if(subProblem==-1) continue; res=Math.min(res,subProblem+1); } memo[amount]=(res==Integer.MAX_VALUE)?-1:res; return memo[amount]; } }
-
法二:dp数组迭代
class Solution { public int coinChange(int[] coins, int amount) { //因为要从0开始考虑 int[] dp=new int[amount+1];//dp[i]表示凑齐i这么多钱要的最少的硬币数量 //初始化一个不可能的取值 Arrays.fill(dp,amount+1); dp[0]=0; for(int i=0;i<dp.length;i++){ for(int coin : coins){ if(i-coin<0) continue; dp[i]=Math.min(dp[i],dp[i-coin]+1); } } return (dp[amount]==amount+1)?-1:dp[amount]; } }