赞
踩
我们来继续使用我们的框架。
当总金额数为0的时候,所需硬币数为0.
“原问题和子问题中会变化的变量”
根据题意,问题中的变量就是”最少硬币数“。
“导致状态变化的行为”
在这里就是”选择不同面额的硬币“
一般来说,都是题中问什么,dp函数设成什么。在这里定义dp函数为:
dp(n) : 凑成总金额 n 所需的硬币数。
接下来我们就可以写出状态转移方程,同时记得使用 [分解问题] 的思想。
dp(n) = dp(n - i) + 1 // 凑成金额n所需硬币数 = 凑成金额(n-i)所需硬币数 + 选用的面值为i的硬币
在实际使用中,因为做选择时是从硬币集合中做选择,所以要将硬币集合也作为一个参数。
- class Solution {
- public int coinChange(int[] coins, int amount) {
- return dp(coins, amount);
- }
- //dp函数返回值的定义:凑出金额amount的硬币数
- public int dp(int[] coins, int amount){
- //base case
- if(amount == 0){
- return 0;
- }
- if(amount < 0){
- return -1;
- }
- 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);
- }
- return res == Integer.MAX_VALUE ? -1 : res;
- }
- }

- class Solution {
- int[] memo;
-
- public int coinChange(int[] coins, int amount) {
- memo = new int[amount + 1];
- Arrays.fill(memo,-666);
- return dp(coins,amount);
- }
- int dp(int[] coins, int amount){
- /*使用备忘录数组后,base case 出现了新的条件*/
- if (amount == 0) return 0;
- if (amount < 0) return -1;
- if (memo[amount] != -666){
- 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];
- }
- }

- class Solution {
- int coinChange(int[] coins, int amount) {
- int[] dp = new int[amount + 1];
- Arrays.fill(dp, amount + 1);
-
- // base case
- dp[0] = 0;
- // 外层 for 循环在遍历所有状态的所有取值
- /*外层循环什么意思?是金额!
- dp数组的下标是金额,dp数组的元素是硬币数*/
- for (int i = 0; i < dp.length; i++) {
- // 内层 for 循环在求所有选择的最小值
- for (int coin : coins) {
- // 子问题无解,跳过
- if (i - coin < 0) {
- continue;
- }
- dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
-
- }
- }
- return (dp[amount] == amount + 1) ? -1 : dp[amount];
- }
- }

为啥 dp 数组中的值都初始化为 amount + 1 呢,因为凑成 amount 金额的硬币数最多只可能等于 amount(全用 1 元面值的硬币),所以初始化为 amount + 1 就相当于初始化为正无穷,便于后续取最小值。
为啥不直接初始化为 int 型的最大值 Integer.MAX_VALUE 呢? 因为后面有 dp[i - coin] + 1,这就会导致整型溢出。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。