小学生都能看懂的题解 | #兑换零钱(一)#
我们可以通过动态规划来解决这个问题,目标是找到组成指定金额 aim 的最少货币数。我们将利用一个动态规划数组 dp 来存储每个金额对应的最小货币数量。
动态规划思路
-
定义状态:
- 定义一个数组
dp,其中dp[i]表示组成金额i所需的最少货币数。
- 定义一个数组
-
初始化:
dp[0] = 0,表示金额为0时不需要任何货币。- 对于其他金额,初始值设置为一个很大的数(比如
Integer.MAX_VALUE),表示暂时无法组成这些金额。
-
状态转移:
- 对于每种货币面值
coin,我们遍历dp数组从coin到aim,更新dp[i]:- 如果使用当前的面值
coin,那么可以通过dp[i - coin] + 1来更新dp[i],意思是我们需要再加上 1 张当前面值的货币。
- 如果使用当前的面值
- 对于每种货币面值
-
结果判断:
- 如果
dp[aim]仍然是初始的很大数值,说明无法组成aim,返回-1。 - 否则,返回
dp[aim]的值。
- 如果
代码实现
下面是完整的 Java 代码实现:
public class Solution {
public int minCoins(int[] arr, int aim) {
// 边界情况:如果 aim 为 0,则需要 0 张货币
if (aim == 0) {
return 0;
}
// 创建 DP 数组,长度为 aim + 1
int[] dp = new int[aim + 1];
// 初始化 DP 数组
for (int i = 1; i <= aim; i++) {
dp[i] = Integer.MAX_VALUE; // 设置为一个很大的数,表示不可达
}
// 动态规划填充 DP 数组
for (int coin : arr) {
for (int i = coin; i <= aim; i++) {
if (dp[i - coin] != Integer.MAX_VALUE) { // 检查是否可达
dp[i] = Math.min(dp[i], dp[i - coin] + 1); // 更新最小货币数
}
}
}
// 返回结果,如果无法组成 aim 则返回 -1
return dp[aim] == Integer.MAX_VALUE ? -1 : dp[aim];
}
}
代码解析
-
public int minCoins(int[] arr, int aim):方法接受一个货币面值的数组arr和目标金额aim。 -
边界检查:
- 如果
aim是 0,直接返回 0,因为不需要任何货币。
- 如果
-
创建和初始化
dp数组:dp[0]默认是 0,表示组成金额 0 的最小货币数。- 其他金额初始化为
Integer.MAX_VALUE,表示初始时无法达到这些金额。
-
动态规划逻辑:
- 对每个面值
coin,遍历dp数组更新对应的值。 - 如果
dp[i - coin]不是不可达的(不等于Integer.MAX_VALUE),就更新dp[i]。
- 对每个面值
-
结果判断:
- 最后,如果
dp[aim]仍为不可达的状态,返回-1,否则返回dp[aim]。
- 最后,如果
时间复杂度和空间复杂度
- 时间复杂度:
O(n * aim),其中n是数组的大小,aim是目标金额。我们需要对每个面值进行遍历,同时更新aim的每个可能的值。 - 空间复杂度:
O(aim),使用了一个数组dp来存储从 0 到aim的解码结果。
示例运行
-
输入
arr = [5, 2, 3]和aim = 20:- 可能的组合是:
5 + 5 + 5 + 5,或者用其他方式组合,总共需要 4 张货币。 - 输出
4。
- 可能的组合是:
-
输入
arr = [5, 2, 3]和aim = 0:- 不需要任何货币。
- 输出
0。
-
输入
arr = [3, 5]和aim = 2:- 无法组成
2,所以返回-1。
- 无法组成
小学生都能看懂的算法 文章被收录于专栏
主要面向小白的算法文章。以小学生都能看懂为目标而编写,顺便巩固下自己。
查看1道真题和解析