首页 > 试题广场 >

兑换零钱(二)

[编程题]兑换零钱(二)
  • 热度指数:2338 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个整数数组 nums 表示不同数额的硬币和一个正整数 target 表示总金额,请你计算并返回可以凑出总金额的的组合数。如果凑不出 target 则返回 0。

数据范围:数组长度满足 ,数组中的数和 target 大小满足
示例1

输入

5,[1,2,4,5]

输出

5
示例2

输入

5,[4]

输出

0
示例3

输入

5,[5]

输出

1
老套路,暴力递归->记忆化搜索->动态规划

暴力递归->记忆化搜索

从左往右尝试,depth表示当前第depth个面额及其之后的面额可以随意使用,rest为还剩下多少钱要凑。在递归的过程中有如下三种情况:
  1. rest=0,就表示形成了一种有效的方案;
  2. rest<0,当前的凑钱方案不合法,直接返回0;
  3. rest>0,使用递归从当前depth开始的面额继续凑目标值。
代码就不详细写了,直接用缓存法改一版记忆化搜索,防止递归展开的过程中重复计算。
private int recurrent(int[] nums, int depth, int rest, int[][] mem) {
    if(rest < 0){
        return 0;
    }
    if(rest == 0){
        return 1;
    }
    if(mem[depth][rest] > 0) return mem[depth][rest];
    for(int i = depth; i < nums.length; i++){
        mem[depth][rest] += recurrent(nums, i, rest - nums[i], mem);
    }
    return mem[depth][rest];
}

动态规划

然后根据递归的逻辑可以修改出动态规划的过程,dp[i][j]表示利用从i到最后一种面额凑j能有多少种方案。根据递归函数中的递归头确定base case,dp数组的第一列为1。根据递归中depthrest的取值可以发现dp[i][j]依赖左边和下面的值,因此从下往上,从左往右填表。
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param target int整型 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int change (int target, int[] nums) {
        // write code here
        int n = nums.length;
        int[][] dp = new int[n][target + 1];
        for(int i = 0; i < n; i++){
            dp[i][0] = 1;
        }
        for(int i = n - 1; i >= 0; i--){
            for(int j = nums[i]; j <= target; j++){
                dp[i][j] = (i < n - 1? dp[i + 1][j]: 0) + dp[i][j - nums[i]];
            }
        }
        return dp[0][target];
    }
}
最后我们注意到,dp[i][j]只依赖其下一行及左边的值,因此还可以进行空间压缩,dp用一维数组就可以,这也就是我们经常看到的背包问题解法。
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param target int整型 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int change (int target, int[] nums) {
        // write code here
        int n = nums.length;
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for(int i = n - 1; i >= 0; i--){
            for(int j = nums[i]; j <= target; j++){
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
}

编辑于 2021-12-15 16:07:43 回复(0)

问题信息

难度:
1条回答 1592浏览

热门推荐

通过挑战的用户

查看代码