1. 零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2:

输入: coins = [2], amount = 3 输出: -1

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/coin-change 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路一:暴力递归

假设coins数组中包含1 5 20 25四种硬币,则amout个硬币所需的最小硬币个数为 amount-1, amount - 5, amout - 20, amount - 25中最小硬币个数再 +1。 自顶向下调用,出现了重叠子问题。 代码如下:

1
2
3
4
5
6
7
	static int coinChange(int n) {
		if (n < 1) return Integer.MAX_VALUE;
		if (n == 25 || n == 20 || n == 5 || n == 1) return 1;
		int min1 = Math.min(coins1(n - 25), coins1(n - 20));
		int min2 = Math.min(coins1(n - 5), coins1(n - 1));
		return Math.min(min1, min2) + 1;
	}
思路二:记忆化搜索

跟上述代码思想类似,自顶向下调用,用dp数组记录计算过的值。 代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
	
	/**
	 * 记忆化搜索(自顶向下的调用)
	 */
	static int coins2(int n) {
		if (n < 1) return -1;
		int[] dp = new int[n + 1];
		int[] faces = {1, 5, 20, 25};
		for (int face : faces) {
			if (n < face) break;
			dp[face] = 1;
		}
		return coins2(n, dp);
	}
	
	static int coins2(int n, int[] dp) {
		if (n < 1) return Integer.MAX_VALUE;
		if (dp[n] == 0) {
			int min1 = Math.min(coins2(n - 25, dp), coins2(n - 20, dp));
			int min2 = Math.min(coins2(n - 5, dp), coins2(n - 1, dp));
			dp[n] = Math.min(min1, min2) + 1;
		}
		return dp[n];
	}
思路三:动态规划

采用动态规划,自下向上递推出最终结果。

定义状态:dp数组中存放,当获取i个数量的零钱时,所需最小的硬币个数。

定义初始值:最初数组中存放比amout还大的数,用于最终判断如果>amount,说明凑不到零钱,则返回-1。 赋值dp[0] = 0;

定义状态转移方程:把coins数组遍历,dp[i] 为 dp[i - coints[0]],dp[i - coints[1]],dp[i - coints[2]]…中的最小值 + 1;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public static int coinChange(int[] coins, int amount) {

        if (amount < 1) return 0;

        // 定义状态 凑到 amount 硬币需要的最少硬币个数
        int[] dp = new int[amount + 1];

        // 定义初始值
        Arrays.fill(dp,amount + 1);
        dp[0] = 0;
        
        // 定义状态转移方程
        for (int i = 1; i <= amount; i++) {

            for (int j = 0; j < coins.length; j++) {
                if (i >= coins[j]) dp[i] = Math.min(dp[i - coins[j]] + 1,dp[i]);
            }
        }

        return dp[amount] > amount ? -1 : dp[amount];
    }

时间复杂度: O(N)

空间复杂度: O(N)