用Python实现找零问题的动态规划代码
时间: 2025-07-14 07:55:44 浏览: 4
<think>嗯,用户让我介绍一下用Python实现找零问题的动态规划代码。首先,我得回忆一下找零问题的动态规划解法。找零问题应该是给定不同的硬币面额,计算凑出某个金额所需的最少硬币数。对吧?
动态规划通常需要确定状态转移方程。那这个问题的状态应该是什么?比如,dp[i]表示凑出金额i所需的最少硬币数。那初始条件的话,dp[0] = 0,因为凑0元不需要硬币。其他金额初始可以设为无穷大,表示暂时无法凑出。
然后,状态转移方程应该是怎样的呢?对于每个金额i,遍历每个硬币面额coin,如果coin小于等于i的话,那么dp[i] = min(dp[i], dp[i - coin] + 1)。这样每次尝试用当前硬币来更新最小值。对吧?
接下来,我需要用Python代码来实现这个思路。首先定义一个函数,比如叫coin_change,接收硬币列表和总金额。然后初始化一个dp数组,长度是总金额加1,初始值设为无穷大。然后dp[0] = 0。
然后循环从1到总金额,每个i都要遍历所有硬币,看看能不能用硬币来减少数量。这里要注意,硬币可能有重复的面额,不过动态规划处理时会自动处理这种情况,所以不需要去重。
测试一下例子,比如coins = [1, 2, 5],amount = 11,应该返回3,因为5+5+1。那代码是否正确呢?比如当i=1时,coin=1,dp[1] = min(inf, dp[0]+1) = 1。i=2时,用coin=2的话,dp[2] = dp[0]+1=1,或者用两个1,但这里会更优。这样下去,最后返回dp[amount],如果是无穷大的话返回-1。
然后用户可能需要代码的示例,以及解释。另外,可能要考虑一些边界情况,比如金额为0时返回0,或者硬币列表为空的情况。这时候需要处理这些情况吗?比如在代码中,如果amount是0,直接返回0。如果硬币列表为空且amount>0,返回-1。
那现在把这些思路写成代码。测试一下是否正确。比如另一个例子,coins = [2], amount =3,应该返回-1。而coins = [1,3,4], amount=6,最少是2个3。用代码的话,dp[6] = min(dp[6-3]+1, ...) 即dp[3]是1,所以结果是2。
可能的错误点在于初始化的时候,是否用float('inf'),然后在比较的时候是否正确。另外,循环的顺序是否正确,先遍历金额,再遍历硬币。
然后用户可能需要解释代码的步骤。所以分步骤讲解,比如初始化、填表、返回结果。
是否要考虑硬币列表中有面额大于当前金额的情况?比如当前i是3,硬币面额是5的话,这时候coin > i,会被跳过,不影响。所以代码中的条件判断是否必要?是的,必须判断coin <= i。
然后把这些思路整理成中文的回答,结构清晰,分步骤说明,并给出代码示例。可能还要提到时间复杂度和空间复杂度。时间复杂度是O(amount * len(coins)),空间是O(amount)。
最后检查代码是否符合要求,比如使用动态规划,是否处理了无法找零的情况,返回-1。还有代码中是否有语法错误,比如Python中的range是否正确,列表初始化是否正确。</think>### 用Python实现找零问题的动态规划代码分步解析
---
#### **问题定义**
**找零问题**:给定硬币面额列表 `coins` 和目标金额 `amount`,求凑出金额 `amount` 所需的最少硬币数量。若无法凑出,返回 `-1`。
---
#### **动态规划思路**
1. **定义状态**
- $dp[i]$:表示凑出金额 $i$ 所需的最少硬币数。
2. **初始化**
- $dp[0] = 0$(金额为0时不需要硬币)
- 其他 $dp[i]$ 初始化为一个极大值(如 $\infty$),表示暂时无法凑出。
3. **状态转移方程**
对于每个金额 $i$,遍历所有硬币面额 $coin$:
$$ dp[i] = \min(dp[i], dp[i - coin] + 1) \quad (\text{需满足} \,\, coin \leq i) $$
4. **结果判断**
- 若 $dp[amount]$ 仍为 $\infty$,说明无法凑出,返回 `-1`;否则返回 $dp[amount]$。
---
#### **Python代码实现**
```python
def coin_change(coins, amount):
# 初始化dp数组:dp[i]表示凑出金额i所需的最少硬币数
dp = [float('inf')] * (amount + 1)
dp[0] = 0 # 初始条件
# 动态规划填表
for i in range(1, amount + 1):
for coin in coins:
if coin <= i: # 仅当硬币面额不超过当前金额时有效
dp[i] = min(dp[i], dp[i - coin] + 1)
# 判断是否有解
return dp[amount] if dp[amount] != float('inf') else -1
```
---
#### **代码解释**
1. **初始化**
```python
dp = [float('inf')] * (amount + 1)
dp[0] = 0
```
- 创建长度为 `amount + 1` 的数组,初始值为无穷大(表示不可达)。
- 明确边界条件:凑出金额0需要0个硬币。
2. **填表过程**
```python
for i in range(1, amount + 1):
for coin in coins:
if coin <= i:
dp[i] = min(dp[i], dp[i - coin] + 1)
```
- 外层循环遍历所有金额 `i`(从1到`amount`)。
- 内层遍历硬币面额,若硬币面额不超过当前金额,则尝试更新 `dp[i]`。
3. **结果返回**
```python
return dp[amount] if dp[amount] != float('inf') else -1
```
- 若最终 `dp[amount]` 仍为无穷大,说明无解,返回 `-1`;否则返回最少硬币数。
---
#### **示例测试**
```python
# 测试案例1
coins = [1, 2, 5]
amount = 11
print(coin_change(coins, amount)) # 输出3(5+5+1)
# 测试案例2
coins = [2]
amount = 3
print(coin_change(coins, amount)) # 输出-1(无法凑出3)
```
---
#### **复杂度分析**
- **时间复杂度**:$O(n \times k)$,其中 $n$ 是目标金额 `amount`,$k$ 是硬币种类数。
- **空间复杂度**:$O(n)$,用于存储 `dp` 数组。
阅读全文
相关推荐




















