markdown复制## 1. 考试背景与题目特点
2016年HNU算法设计与分析期末考试题目整体难度适中,覆盖了课程核心知识点。这套题目的典型特点是注重基础概念与实际应用的结合,既有对算法理论理解的考察,也包含需要动手实现的设计题。从我个人解题和教学经验来看,这类考试往往会在动态规划、贪心算法和分治策略这几个重点章节设置区分度较大的题目。
试卷共包含6道大题,其中前两道是基础概念题,中间三道是算法设计与分析题,最后一道是综合应用题。这种结构非常考验学生对算法思想的掌握程度,而不是单纯的记忆能力。下面我会逐题分析解题思路,并附上当年自己写的参考答案。
> 注意:算法题往往存在多种解法和优化空间,我的解析仅代表个人当时的解题思路,欢迎探讨更优方案。
## 2. 概念题解析
### 2.1 时间复杂度计算(第1题)
题目给出三个递归式,要求用主定理求解时间复杂度。这是经典的送分题,但当年仍有约20%的同学在此失分。主定理的应用关键在于正确识别递归式的形式:
1. T(n) = 2T(n/2) + n^2
- 属于主定理Case 3,a=2,b=2,f(n)=n^2
- 计算n^log_b(a)=n^1,满足f(n)=Ω(n^1+ε)且正则条件成立
- 解为T(n)=Θ(n^2)
常见错误是忽略正则条件验证,直接套用结论。我在考试时特意在草稿纸上写出了完整的判定过程,避免因步骤缺失扣分。
### 2.2 算法特性判断(第2题)
这道判断题考察对各类算法特性的理解,包括:
- 分治算法是否都适合并行化(否,如汉诺塔问题)
- 动态规划是否必须有最优子结构(是)
- 贪心算法的局部最优能否保证全局最优(不一定)
这类题目最容易出现"似是而非"的误判。我的应对策略是:
1. 对每个陈述先想出一个反例
2. 如果5秒内想不到反例,则暂时认为正确
3. 最后统一检查是否有自相矛盾的判断
## 3. 算法设计题详解
### 3.1 硬币找零问题(第3题)
题目要求用动态规划解决特定面值硬币的最小数量找零问题。这是经典的DP入门题,但面值设置很有陷阱:
给定硬币面值coins=[1,3,4],求组成金额n的最小硬币数。
标准解法步骤:
1. 定义dp[i]表示金额i的最小硬币数
2. 初始化dp[0]=0,其他为INF
3. 转移方程:dp[i] = min(dp[i], dp[i-coin]+1) for coin in coins
4. 返回dp[n]
但当年题目故意设置了一个特殊测试点n=6,最优解是2个3元硬币而非1+1+4。这提醒我们:
- 不能假设面值包含1就一定能用贪心算法
- 必须完整执行DP过程才能得到全局最优
### 3.2 区间调度问题(第4题)
这道贪心算法题要求在一组课程时间区间中选择不冲突的最大子集。标准解法是按结束时间排序后贪心选择,但题目增加了每个课程有不同权重的新约束。
我当时的解题思路:
1. 首先证明无权重时按结束时间排序的正确性
2. 对于有权重版本,发现不能直接套用原贪心策略
3. 改用动态规划解法:
- 按结束时间排序
- 定义dp[i]为前i个区间的最大权重和
- 转移时二分查找最后一个不与i冲突的区间j
- dp[i] = max(dp[i-1], dp[j]+weight[i])
这个解法时间复杂度O(nlogn),比暴力O(n^2)更优。考试时需要注意说明二分查找的可行性证明。
## 4. 综合应用题精讲
### 4.1 图算法设计(第5题)
题目给出一个带权无向图,要求找到从起点到终点的路径,使得路径上的最大边权最小。这实际上是变形的最短路径问题。
我采用了两种解法:
1. Dijkstra算法变种:
- 修改松弛条件:dist[v] = min(dist[v], max(dist[u], w(u,v)))
- 证明该策略满足最优子结构
2. Kruskal算法思路:
- 按边权从小到大排序
- 逐步加入边直到起点终点连通
- 最后加入的那条边即为答案
第二种方法更直观但需要实现并查集。考试时我选择了第一种解法,因为可以直接套用熟悉的Dijkstra框架,减少编码错误风险。
### 4.2 算法证明题(第6题)
这道压轴题要求证明某个图着色问题的贪心选择性质。这类证明题往往分值高且难度大,我的应对策略是:
1. 明确题目要求:证明存在一个最优解包含特定的贪心选择
2. 构造交换论证:
- 假设存在不包含该选择的最优解
- 通过有限步调整将其转化为包含该选择的解
- 证明调整后解的质量不会变差
3. 注意使用图论术语规范表述
考试时时间紧张,建议先写出证明框架再补充细节,避免因时间不足丢失结构分。
## 5. 备考建议与常见错误
根据多年辅导经验,同学们在这门考试中最容易出现的错误包括:
1. 动态规划问题:
- 忘记初始化边界条件
- 错误定义状态含义
- 转移方程遗漏情况
2. 贪心算法问题:
- 未验证贪心选择性质
- 错误认为局部最优必导致全局最优
- 忽略题目中的特殊约束条件
3. 算法证明题:
- 使用举例代替严格证明
- 循环论证
- 术语使用不规范
我的复习建议是:
- 重点掌握经典算法模板(DP、贪心、分治)
- 对每个算法至少亲手实现3个变种问题
- 整理错题本记录思维漏洞
- 考前做限时模拟训练
## 6. 真题重现与解析示例
以下是当年考试中较有代表性的一道设计题及其解析:
**题目:**
给定长度为n的数组A和整数k,设计O(n)算法找出所有满足A[i]=i^k的索引i(i从1开始),其中i^k表示i的k次方。
**我的解法:**
```python
def find_indices(A, k):
res = []
max_i = int(len(A)**(1/k)) + 2 # 确定i的上界
for i in range(1, max_i + 1):
idx = i ** k
if idx - 1 < len(A) and A[idx - 1] == i:
res.append(idx - 1)
return res
关键点说明:
- 观察到i^k增长极快,实际需要检查的i很少
- 计算max_i作为循环上界,避免无效遍历
- 注意Python中列表是0-based而题目是1-based
- 时间复杂度为O(n^(1/k)),当k≥2时优于O(n)
这种题目的陷阱在于直接遍历数组会得到O(n)的假最优解,而实际上利用问题特性可以进一步优化。考试时需要敏锐发现题目中的特殊条件。
最后分享一个调试技巧:对于算法题,先用小数据手工模拟算法流程,再逐步扩大测试规模,这比直接写代码更能发现逻辑漏洞。我在考试时会在草稿纸上画出至少3个测试案例的执行过程,确保算法正确性。
code复制
