Files
SolutionEuler/solutions/0031/readme.md
Sidney Zhang 8ee312ac1d feat:添加欧拉项目第30题和第31题的解决方案
📝 docs:为第31题添加详细的动态规划算法说明文档
2025-12-29 18:18:10 +08:00

115 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 动态规划法
## 核心思路
这个函数采用**动态规划**的思想,通过构建一个 `ways` 数组来累积计算每个金额对应的方法数。
---
## 逐行解析
### 1. 初始化
```python
coins = [1, 2, 5, 10, 20, 50, 100, 200]
ways = [1] + [0] * 200 # 结果是 [1, 0, 0, 0, ..., 0] (共201个元素)
```
- `coins`: 所有可用的硬币面值(单位:便士)
- `ways[i]`: 表示凑成金额 `i` 的方法数
- **关键点**`ways[0] = 1` 表示凑成0元有1种方法什么都不用这是动态规划的**基准条件**
### 2. 双重循环的核心逻辑
```python
for coin in coins: # 按顺序遍历每种硬币
for i in range(coin, 201): # 从当前硬币面值遍历到200
ways[i] += ways[i - coin]
```
这就是**状态转移方程**,其含义是:
> **凑成金额 `i` 的方法数 = 原来方法数 + 使用当前硬币的方法数**
其中 `ways[i - coin]` 表示在使用了1枚当前硬币后凑齐剩余金额的方法数。
---
## 具体执行过程演示
让我们跟踪计算前几个值的变化,以理解算法如何工作:
### 初始状态
```
ways = [1, 0, 0, 0, 0, 0, ...] # ways[0]=1
```
### 第1轮使用硬币 1p
```python
for i in range(1, 201):
ways[i] += ways[i-1]
```
- `i=1`: `ways[1] += ways[0]``0 + 1 = 1` ✓ 凑1p: {1}
- `i=2`: `ways[2] += ways[1]``0 + 1 = 1` ✓ 凑2p: {1,1}
- `i=3`: `ways[3] += ways[2]``0 + 1 = 1` ✓ 凑3p: {1,1,1}
- ...
- **结果**只用1p硬币每个金额都有且仅有1种方法
### 第2轮加入硬币 2p
```python
for i in range(2, 201):
ways[i] += ways[i-2]
```
关键更新点:
- `i=2`: `ways[2] += ways[0]``1 + 1 = 2`
- 原来:{1,1}
- 新增:{2}
- `i=3`: `ways[3] += ways[1]``1 + 1 = 2`
- 原来:{1,1,1}
- 新增:{1,2}
- `i=4`: `ways[4] += ways[2]``1 + 2 = 3`
- 原来:{1,1,1,1}
- 新增:{1,1,2}, {2,2}
### 第3轮加入硬币 5p
当加入5p硬币后凑5p的方法从1种 {1,1,1,1,1} 变成2种新增 {5}。
**以此类推**,直到处理完所有硬币类型。
---
## 为什么这样计算?
这个算法的精妙之处在于:
1. **按硬币顺序处理**:确保计算的是**组合数**(不考虑顺序)
- {1,2,2} 和 {2,1,2} 视为同一种方法
- 如果调换循环顺序(先金额后硬币),会计算出排列数
2. **累积效应**`ways` 数组保存的是**所有已处理硬币**能凑成的方法总数
3. **避免重复**:每种硬币只被考虑一次在其对应的外层循环中,确保不重复计算
---
## 最终结果
当循环结束后,`ways[200]` 的值就是 200便士能被凑成的所有方法数。
```python
print(f"\nThe number of ways to make £2 is {ways[200]:,d}")
```
输出结果是:**73682**
这意味着用这8种英国硬币凑成2英镑共有**73,682**种不同的方式。
---
## 时间复杂度
- **O(N×M)**:其中 N = 硬币种类数8M = 目标金额200
- 实际计算量约为 8 × 200 = 1600 次操作,效率极高
这个算法简洁优雅,展示了动态规划在组合计数问题中的强大威力。