2022—2023 学年第一学期期末 A 卷(有图论)
一、复杂度填空题
- 的渐近阶;
- 的渐近阶;
- 在包含 个元素的数组上运行快速排序,填写运行时间;
- 顶点无向完全图的最小生成树有多少条边。
查看填空答案与题面说明
- 。
- 。主定理中递归项占主导。
- 现有原卷没有说明枢轴策略,也没说问最好、平均还是最坏时间,因此无法唯一作答。若按课程常用平均时间,则对 个元素为 ;最坏情况仍可为 。不应把缺失条件偷偷补上。
- 恰有 条边,渐近为 。
二、判断题
- 任何 NPC 问题和任何 NP-hard 问题都是 NP 问题。
- 。
- 若能在 求最大独立集,则所有 NPC 问题都能在多项式时间解决。
- 判断图中是否有大小为 5 的团无法在多项式时间解决。
查看判断题答案
- 错误。NPC 一定属于 NP;NP-hard 问题不一定属于 NP,甚至不一定是判定问题。
- 无法判断。若 ,交集为空;若 ,NPC 问题也在 P 中。
- 正确。最大独立集的判定版本 NP 完全;若它有多项式算法,则所有 NP 问题经规约都有多项式算法。
- 错误。5 是固定常数,枚举所有五元顶点组需要 ,仍为多项式时间。
三、强连通分量运行实例

从顶点 开始模拟强连通分量算法:
- 在反向图 上执行 DFS,按完成时刻从早到晚写出顶点顺序;
- 再按该顺序的逆序在原图 上执行 DFS,写出各顶点的发现时间、完成时间和每个强连通分量的顶点集合。
查看强连通分量与一种合法 DFS 过程
图的强连通分量为
若每个邻接表按字母顺序访问,在反向图 从 开始,一种从早到晚的完成顺序是
h, i, g, f, d, c, e, b, a
再按完成时间逆序在原图启动 DFS,依次得到上述三个分量。题面没有规定同一顶点多条出边的访问顺序,因此发现/完成时间并非唯一;答案应与自己声明的邻接顺序一致,但 SCC 集合不变。
继续采用“邻接表按字母顺序访问”,并令计时器从 0 开始、每次发现或完成一个顶点时先加 1。第二遍 DFS 的发现/完成时间为:
| 顶点 | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| 发现时间 | 1 | 2 | 4 | 9 | 5 | 10 | 13 | 14 | 15 |
| 完成时间 | 8 | 3 | 7 | 12 | 6 | 11 | 18 | 17 | 16 |
三棵 DFS 树的根依次为 ,它们分别给出集合 {a,b,c,e}、{d,f} 和 {g,h,i}。
四、Dijkstra 运行实例

- 从顶点 出发模拟 Dijkstra 算法,填写每轮确定的顶点和各顶点暂定距离;
- 若把边 的权重改为 ,判断能否继续使用 Dijkstra;若不能,写出一种可行算法。
查看距离更新与负边问题
从 出发,一种确定顶点顺序为
a, b, e, c, g, h, f, d
下表把每一行定义为“选定该行顶点,并完成它的出边松弛之后”的暂定距离;初始行还没有选定顶点:
| 本轮确定 | ||||||||
|---|---|---|---|---|---|---|---|---|
| 初始 | 0 | |||||||
| 0 | 2 | 7 | ||||||
| 0 | 2 | 7 | 5 | 8 | ||||
| 0 | 2 | 7 | 15 | 5 | 8 | 12 | ||
| 0 | 2 | 7 | 15 | 5 | 8 | 12 | ||
| 0 | 2 | 7 | 15 | 5 | 8 | 10 | ||
| 0 | 2 | 7 | 15 | 5 | 11 | 8 | 10 | |
| 0 | 2 | 7 | 13 | 5 | 11 | 8 | 10 | |
| 0 | 2 | 7 | 13 | 5 | 11 | 8 | 10 |
因此最终最短距离为:
| 顶点 | ||||||||
|---|---|---|---|---|---|---|---|---|
| 距离 | 0 | 2 | 7 | 13 | 5 | 11 | 8 | 10 |
若把 改成 ,不能继续使用 Dijkstra,因为已确定顶点的距离可能被后来负边再次降低。可改用 Bellman–Ford;该图若确认无环,也可按拓扑序松弛。
五、定长闭区间覆盖点集
数轴上有 个不同点 。使用若干个长度为 的闭区间覆盖全部点,求最少区间数和每个区间的范围。要求给出伪代码和复杂度, 可得满分。
查看贪心算法
先把点坐标升序排序。取最左边尚未覆盖的点 ,放置区间
并跳过其中所有点,重复直到结束。
任何可行解都必须用某个区间覆盖当前最左点。把该区间右移到左端恰为 不会丢失右侧覆盖能力,因此存在最优解包含贪心选择。排序 ,扫描 。
sort(x[1..n])
intervals = empty list
i = 1
while i <= n:
left = x[i]
right = left + l
append [left, right] to intervals
i = i + 1
while i <= n and x[i] <= right:
i = i + 1
return intervals
六、在 中找第一个 1
数组 由连续的若干个 0 和连续的若干个 1 构成,并保证两种数字都出现:
- 在 时间找到第一个 1 的位置;
- 若 1 的个数 ,在 时间找到第一个 1,并给出伪代码。
查看 $O(\log n)$ 与 $O(\log m)$ 算法
标准方法对单调谓词 A[i] == 1 二分,找第一个为真的位置,时间 。
left = 1
right = n
while left < right:
mid = floor((left + right) / 2)
if A[mid] == 1:
right = mid
else:
left = mid + 1
return left
若 1 的个数 ,从数组末尾向左做指数搜索:依次检查距离末尾 的位置,直到看到 0 或越过数组。第一个 1 位于最后两个检查位置之间,再二分该区间。指数搜索只走到 的后缀,时间 。
knownOne = n
step = 1
while n - step >= 1 and A[n - step] == 1:
knownOne = n - step
step = 2 * step
knownZero = max(1, n - step)
left = knownZero + 1
right = knownOne
while left < right:
mid = floor((left + right) / 2)
if A[mid] == 1:
right = mid
else:
left = mid + 1
return left
题目保证 0 和 1 都出现,所以当指数搜索越过数组左端时,位置 1 一定可以作为 knownZero。指数阶段和二分阶段处理的范围都只有 。
七、最大汇聚度生成树
定义树 的汇聚度为其最大顶点度数
给定连通无向图 ,构造一棵汇聚度最大的生成树,写出伪代码并分析复杂度; 可得满分。
查看线性算法与证明
先扫描所有边,找原图中度数最大的顶点 ,其度为 。把 的所有关联边加入生成树候选,此时形成一棵以 为中心的星形局部树,不会成环。
再从已连接顶点出发做 BFS/DFS,每遇到一个尚未进入树的顶点就加入发现它的边,直到覆盖全图。原图连通,所以一定能扩展成生成树。
v = a vertex with maximum length(Adj[v])
T = empty edge set
visited[u] = false for every u in V
Q = empty queue
visited[v] = true
enqueue(Q, v)
for each w in Adj[v]:
if not visited[w]:
visited[w] = true
add edge (v, w) to T
enqueue(Q, w)
while Q is not empty:
u = dequeue(Q)
for each w in Adj[u]:
if not visited[w]:
visited[w] = true
add edge (u, w) to T
enqueue(Q, w)
return T
所得树中 。任何生成树都是原图子图,任何顶点在树中的度都不可能超过它在原图中的度,因此这已经达到全局上界。时间 。
八、整袋糖果均分
老师有 袋不能拆开的糖果,第 袋有 颗,且 :
- 当 是 2 的倍数时,判断能否均分给两人;若可以,输出分配方案,目标复杂度 ;
- 当 是 3 的倍数时,判断能否均分给三人,目标复杂度 。
查看两人和三人的动态规划
两人均分时目标为 。做 0-1 子集和:
保留前驱即可恢复第一人的袋子,剩余袋子给第二人。时间 、空间 ;只判断可压为 。
三人均分时目标 ,只显式记录前两人的和:
第 袋可给第一人、第二人或第三人。若 为真,剩余总和也恰为 。时间 、空间可滚动为 。