零钱兑换
难度:
标签:
题目描述
English description is not available for the problem. Please switch to Chinese.
代码结果
运行时间: 64 ms, 内存: 16.0 MB
/*
* 思路:
* 1. 使用动态规划的方法,定义一个 dp 数组,dp[i] 表示凑成金额 i 所需的最少硬币个数。
* 2. 使用 IntStream 和 Arrays.stream 来实现动态规划的步骤。
* 3. 初始化 dp[0] = 0,表示凑成金额 0 所需的硬币个数为 0。
* 4. 对于每个金额 i,从 1 到 amount,尝试用每个硬币去更新 dp[i]。
* 5. 如果当前硬币的面值不大于 i,则 dp[i] = Math.min(dp[i], dp[i - coin] + 1)。
* 6. 最后,返回 dp[amount],如果 dp[amount] 仍然是初始值,则返回 -1 表示无法凑成该金额。
*/
import java.util.*;
import java.util.stream.*;
public class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
IntStream.rangeClosed(1, amount).forEach(i -> {
Arrays.stream(coins).forEach(coin -> {
if (coin <= i) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
});
});
return dp[amount] > amount ? -1 : dp[amount];
}
}
解释
方法:
题解中采用了一种基于位运算的动态规划方法来解决零钱兑换问题。首先,使用一个整数`dp`的位表示,其中第i位的值为1表示能够用硬币组合凑成金额i。初始化`dp`为1左移`amount`位,表示只有amount位为1,其余均为0。使用一个无限循环,每次循环中检查第0位,如果为1,则返回当前的步数(已经找到最小硬币组合)。对于每种硬币面值,将`dp`右移该硬币面值的位数,通过位或操作更新`dp`,如果在某次循环中`dp`未发生变化,说明无法通过已有硬币组合凑出更多金额,此时返回-1。
时间复杂度:
O(amount * k)
空间复杂度:
O(1)
代码细节讲解
🦆
在题解中使用位运算和动态规划结合的方法较为少见,你能解释为什么选择使用位运算来表示状态而不是更常见的数组或哈希表吗?
▷🦆
题解中提到,如果`dp`在某次循环后未发生变化则返回-1。在什么情况下会出现`dp`未变化的情况,能否给出一个具体的例子?
▷🦆
初始化`dp`为1左移`amount`位,这种初始化方式有什么特别的意义吗?为什么选择将第`amount`位设置为1而其他位为0?
▷