1. 零钱兑换问题解析
零钱兑换是动态规划中的经典问题,也是面试中的高频考点。这个问题看似简单,却蕴含着深刻的算法思想。想象一下你是一个收银员,现在需要找零11元,手头有1元、2元和5元的硬币,如何用最少数量的硬币完成找零?这就是零钱兑换问题的现实场景。
在实际开发中,类似的问题随处可见:比如游戏中的道具合成系统、网络传输中的分包策略等。理解这个问题的解法,不仅能帮助我们通过算法面试,更能培养解决实际工程问题的思维模式。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题分析与解法思路
2.1 问题重述
给定不同面额的硬币数组coins和一个总金额amount,计算凑成总金额所需的最少硬币个数。每种硬币可以使用无限次,如果无法凑出目标金额则返回-1。
2.2 暴力递归解法
最直观的解法是暴力递归:对于目标金额amount,尝试所有可能的硬币组合,选择硬币数量最少的方案。这种方法虽然简单,但时间复杂度高达O(S^n),其中S是金额,n是硬币种类数,在amount较大时完全不可行。
java复制public int coinChange(int[] coins, int amount) {
if (amount == 0) return 0;
if (amount < 0) return -1;
int minCoins = Integer.MAX_VALUE;
for (int coin : coins) {
int res = coinChange(coins, amount - coin);
if (res >= 0 && res < minCoins) {
minCoins = res + 1;
}
}
return minCoins == Integer.MAX_VALUE ? -1 : minCoins;
}
2.3 动态规划解法
动态规划是解决这类问题的利器。我们可以定义一个数组dp,其中dp[i]表示凑出金额i所需的最少硬币数。状态转移方程为:
dp[i] = min(dp[i - coin] + 1) for coin in coins
初始条件为dp[0] = 0,表示凑出金额0需要0个硬币。
java复制public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1); // 初始化为一个不可能的大值
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (coin <= i) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
这个解法的时间复杂度为O(S*n),空间复杂度为O(S),其中S是金额,n是硬币种类数。
3. 算法优化与细节处理
3.1 边界条件处理
在实际编码中,有几个边界条件需要特别注意:
- amount为0时,直接返回0
- coins数组为空时,返回-1
3
