1. 题目背景与需求分析
这道编程题源自GESP一级认证考试,题目描述为:小明需要购买文具,已知文具店里有三种文具,分别是铅笔、橡皮和笔记本。铅笔每支2元,橡皮每个1元,笔记本每本5元。现在给定小明拥有的钱数n(n≥1),要求计算出小明用这些钱恰好能买多少种不同的文具组合(每种文具可以买0个或多个)。
1.1 问题建模
这是一个典型的组合数学问题,可以抽象为:
给定三个正整数a=2,b=1,c=5,求非负整数解(x,y,z)的个数,使得2x + y + 5z = n成立。
这类问题在计算机科学中被称为线性丢番图方程,是算法竞赛和编程考试中的常见题型。解题关键在于如何高效枚举所有可能的组合情况。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题思路与算法设计
2.1 暴力枚举法
最直观的解法是三重循环枚举所有可能的购买组合:
python复制n = int(input())
count = 0
for x in range(n//2 + 1): # 铅笔最多买n//2支
for y in range(n + 1): # 橡皮最多买n个
for z in range(n//5 + 1): # 笔记本最多买n//5本
if 2*x + y + 5*z == n:
count += 1
print(count)
时间复杂度分析:
- 最外层循环次数:O(n/2)
- 中层循环次数:O(n)
- 内层循环次数:O(n/5)
- 总复杂度:O(n³)
虽然对于n≤100的小规模数据足够,但当n较大时效率会显著下降。
2.2 优化解法:双重循环
观察到当确定x和z后,y的值可以直接计算得出:
python复制n = int(input())
count = 0
for x in range(n//2 + 1):
for z in range(n//5 + 1):
remaining = n - 2*x - 5*z
if remaining >= 0:
count += 1
print(count)
优化效果:
- 减少一层循环
- 时间复杂度降为O(n²)
- 空间复杂度保持O(1)
2.3 数学解法:生成函数
对于数学基础较好的学习者,可以使用生成函数方法:
文具组合对应的生成函数为:
(1 + x² + x⁴ + ...)(1 + x + x² + ...)(1 + x⁵ + x¹⁰ + ...)
求xⁿ项的系数即为解的数量。这种方法适合理论分析,但在编程实现中不如枚举法直观。
3. 代码实现与测试
3.1 Python实现示例
python复制def count_combinations(n):
count = 0
max_pencil = n // 2
max_notebook = n // 5
for pencil in range(max_pencil + 1):
for notebook in range(max_notebook + 1):
remaining = n - 2*pencil - 5*notebook
if remaining >= 0:
count += 1
return count
n = int(input())
print(count_combinations(n))
3.2 边界条件测试
| 输入n | 预期输出 | 测试用例说明 |
|---|---|---|
| 1 | 1 | 只能买1个橡皮 |
| 5 | 4 | 包含买笔记本的情况 |
| 10 | 10 | 多种组合可能 |
| 100 | 5151 | 较大数值验证 |
3.3 常见错误排查
-
循环范围错误:
- 错误写法:
range(n)忘记加1 - 正确写法:
range(n//2 + 1)
- 错误写法:
-
剩余金额判断遗漏:
- 必须检查
remaining >= 0,否则会统计无效组合
- 必须检查
-
初始化问题:
- 忘记初始化计数器
count = 0
- 忘记初始化计数器
4. 算法优化进阶
4.1 单层循环解法
进一步观察可以发现,当固定铅笔数量x后,笔记本数量z的取值范围为:
0 ≤ z ≤ (n-2x)//5
因此可以直接计算每个x对应的z的可能取值数量:
python复制n = int(input())
count = 0
for x in range(n//2 + 1):
max_z = (n - 2*x) // 5
count += max_z + 1
print(count)
优势:
- 时间复杂度降至O(n)
- 代码更简洁高效
4.2 数学公式推导
通过数学推导可以得到闭式解公式,但实现较为复杂,适合数学竞赛而非编程考试。
5. 教学建议与学习路径
5.1 教学步骤建议
- 先引导理解问题本质,建立数学模型
- 从最直观的三重循环开始实现
- 逐步优化算法,讲解每步优化思路
- 最后介绍数学方法拓宽视野
5.2 相关扩展题目
- 硬币找零问题
- 背包问题变种
- 多元一次方程整数解计数
5.3 学习资源推荐
- 《算法竞赛入门经典》枚举算法章节
- LeetCode 322题"Coin Change"
- Project Euler 第31题
在实际教学中,建议先让学生独立尝试暴力解法,再引导他们发现优化空间,最后讨论更高级的数学方法。这种循序渐进的方式最能培养算法思维。
