table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}
159、考虑以下算法。算法 Foo(n)//输入:一个非负整数 nSum ← 0for i ← 1 to n do Sum ← Sum + i/i!return Suma. 这个算法计算的是什么?b. 它的基本操作是什么?c. 基本操作执行了多少次?d. 这个算法的效率类别是什么?e. 提出一个改进方案,或者一个更好的算法,并指出其效率类别。如果你做不到,尝试证明实际上无法做到。
a. 该算法计算的是从 1 到 n 的 i/i! 之和。
b. 基本操作是加法和除法,即
Sum←Sum+ii!Sum←Sum+ii!
中的加法和除法运算。
c. 基本操作执行了 n 次,因为循环从 1 到 n 执行。
d. 效率类别是 $ O(n) $,因为基本操作的执行次数与输入规模 n 呈线性关系。
e. 可提前计算 $ i! $ 的值,避免重复计算,这样仍为 $ O(n) $ 效率类别。因为算法本身就是线性遍历,很难有本质上的效率提升,所以无法找到更优算法。
160、页码编号问题:假设一本书有 1000 页,页码从 1 开始连续编号,求编号所需的十进制数字的总数。
要计算编号所需的十进制数字的总数,可将页码范围分段计算:
1 – 9 页
:每页 1 个数字,共 9 个数字;
10 – 99 页
:每页 2 个数字,有 99 – 10 + 1 = 90 页,共 90 × 2 = 180 个数字;
100 – 999 页
:每页 3 个数字,有 999 – 100 + 1 = 900 页,共 900 × 3 = 2700 个数字;
第 1000 页
:4 个数字。
所以总共需要:
9+180+2700+4=28939+180+2700+4=2893
个十进制数字。
161、考虑以下递归算法。算法 Riddle(A[0..n – 1]) //输入:实数数组 A[0..n – 1] 如果 n = 1 返回 A[0] 否则 temp ← Riddle(A[0..n – 2]) 如果 temp ≤ A[n – 1] 返回 temp 否则返回 A[n – 1] a. 该算法计算什么?b. 为该算法的基本操作计数建立递归关系并求解。
a. 该算法计算数组 A 中的最小值。
b. 设基本操作计数为 $ C(n) $,递归关系为
C(n)=C(n−2)+1(n>1),C(n)=C(n−2)+1(n>1),
其中 $ C(1) = 0 $。
当 $ n $ 为奇数时,
C(n)=n−12;C(n)=n−12;
当 $ n $ 为偶数时,
C(n)=n2−1.C(n)=n2−1.
162、对于一个n × n矩阵A = ⎡ ⎢⎢⎣ a0 0… a0 n−1 a1 0… a1 n−1… … an−1 0… an−1 n−1 ⎤ ⎥⎥⎦,其行列式记为det A。当n = 1时,det A定义为a00;当n > 1时,通过递归公式det A = ∑(j = 0 to n – 1) sja0 j det Aj 定义,其中若j为偶数,sj为+1,若j为奇数,sj为 – 1,a0 j 是第0行第j列的元素,Aj 是从矩阵A中删除第0行和第j列后得到的(n – 1) × (n – 1)矩阵。a. 为实现该递归定义的算法所进行的乘法次数建立递推关系。b. 不解该递推关系,与n!相比,你能对解的增长阶说些什么?
a. 设 $ M(n) $ 为计算 $ n imes n $ 矩阵行列式所需的乘法次数。当 $ n = 1 $ 时,无需乘法,即 $ M(1) = 0 $;当 $ n > 1 $ 时,计算 $ det A $ 的公式中有 $ n $ 项求和,每一项都需要一次乘法($ a_{0j} $ 乘以 $ det A_j $),并且要计算 $ n $ 个 $ (n - 1) imes (n - 1) $ 矩阵的行列式,所以递推关系为
$$ M(n) = n + nM(n - 1) $$
b. 该递推关系的解的增长阶与 $ n! $ 同阶,因为递推式 $ M(n) = n + nM(n - 1) $ 中,随着 $ n $ 的增大,主要增长项是 $ nM(n - 1) $,类似于 $ n! $ 的递归定义 $ n! = n imes (n - 1)! $
163、煎汉堡问题:有n个汉堡要在一次只能放两个汉堡的小烤架上煎。每个汉堡需要煎两面,无论同时煎一个还是两个汉堡,煎一面需要1分钟。考虑以下递归算法以最短时间完成此任务。若n ≤ 2,将一个或两个汉堡一起煎两面;若n > 2,先将任意两个汉堡一起煎两面,然后对剩下的n – 2个汉堡递归应用相同步骤。a. 建立并求解该算法煎n个汉堡所需时间的递推关系。b. 解释为什么该算法并非对所有n > 0都能以最短时间煎好汉堡。c. 给出一个能以最短时间完成任务的正确递归算法。
a. 设煎 $ n $ 个汉堡所需时间为 $ T(n) $。
当 $ n leq 2 $ 时,$ T(n) = 2 $;
当 $ n > 2 $ 时,$ T(n) = T(n – 2) + 2 $。
通过递推可得:
– 若 $ n $ 为偶数,$ T(n) = n $;
– 若 $ n $ 为奇数,$ T(n) = n + 1 $。
b. 当 $ n = 3 $ 时,该算法时间为 4 分钟,但煎三个汉堡的最短时间小于 4 分钟,所以并非对所有 $ n > 0 $ 都能以最短时间完成。
c. 若 $ n leq 2 $,直接煎;
若 $ n > 2 $,先煎两个汉堡的一面,然后换一个新的汉堡和其中一个已煎一面的汉堡一起煎,最后煎剩下两个只煎了一面的汉堡,之后对剩下的 $ n – 3 $ 个汉堡递归应用此步骤。
164、爬楼梯问题。如果每步可以爬一级或两级楼梯,求爬上n级楼梯的不同方法数。例如,爬3级楼梯有三种方法:1 – 1 – 1、1 – 2和2 – 1。
该问题可通过动态规划的思想解决,爬上n级楼梯的不同方法数构成斐波那契数列。设f(n)表示爬上n级楼梯的方法数,则有:
f(1) = 1
f(2) = 2
对于 n > 2,有:
f(n)=f(n−1)+f(n−2)f(n)=f(n−1)+f(n−2)
165、a. 什么是蛮力算法?举例说明。b. 蛮力算法能否用于解决幂运算问题?举例说明。
a. 蛮力算法是一种直接基于问题描述和相关概念定义来解决问题的直观方法。例如计算非零数a的非负整数n次幂,根据幂运算定义,$ a^n = a imes ldots imes a $(n个a相乘),可以通过将1乘以a n次来计算$ a^n $。
b. 可以。例如计算非零数a的非负整数n次幂,根据幂运算定义$ a^n = a imes ldots imes a $(n个a相乘),可以简单地通过将1乘以a n次来计算$ a^n $,这就是用蛮力算法解决幂运算问题。
166、a. 证明如果冒泡排序在遍历列表时没有进行交换操作,那么列表已经排好序,算法可以停止。b. 编写包含此改进的方法的伪代码。c. 证明改进版本在最坏情况下的效率是二次的。
a. 冒泡排序的原理是比较相邻元素并在顺序错误时交换它们。如果在一次遍历中没有发生交换,说明所有相邻元素都是按正确顺序排列的。由于列表中任意两个相邻元素都已按顺序排列,所以整个列表是有序的,算法可以停止。
b. 以下是包含此改进的冒泡排序伪代码:
ALGORITHM ImprovedBubbleSort(A[0..n - 1])
// 用改进的冒泡排序对给定数组进行排序
// 输入:可排序元素的数组 A[0..n - 1]
// 输出:按非降序排序的数组 A[0..n - 1]
swapped ← true
for i ← 0 to n - 2 while swapped do
swapped ← false
for j ← 0 to n - 2 - i do
if A[j + 1] < A[j]
swap A[j] and A[j + 1]
swapped ← true
c. 在最坏情况下,即列表是降序排列时,改进的冒泡排序仍然需要进行和原始冒泡排序几乎相同数量的比较。每次遍历都需要进行交换,直到整个列表排序完成。原始冒泡排序的比较次数是一个二次函数,改进版本在最坏情况下也需要进行类似数量的比较,因此其最坏情况效率仍然是二次的。
167、在笛卡尔平面中,有几种不同的方法来定义两点p1(x1, y1)和p2(x2, y2)之间的距离。特别地,曼哈顿距离定义为dM(p1, p2) = |x1 – x2| + |y1 – y2|。证明dM满足以下公理:dM(p1, p2) = dM(p2, p1)。
已知 $ d_M(p_1,p_2) = |x_1 – x_2| + |y_1 – y_2| $,则
dM(p2,p1)=|x2−x1|+|y2−y1|dM(p2,p1)=|x2−x1|+|y2−y1|
因为绝对值的性质 $ |a| = |-a| $,所以
|x2−x1|=|x1−x2||x2−x1|=|x1−x2|
|y2−y1|=|y1−y2||y2−y1|=|y1−y2|
那么
dM(p2,p1)=|x2−x1|+|y2−y1| =|x1−x2|+|y1−y2| =dM(p1,p2)dM(p2,p1)=|x2−x1|+|y2−y1| =|x1−x2|+|y1−y2| =dM(p1,p2)
即
dM(p1,p2)=dM(p2,p1)dM(p1,p2)=dM(p2,p1)
得证。
168、最近点对问题可以在k维空间中提出,其中两点p′(x′1,…, x′k)和p′′(x′′1,…, x′′k)之间的欧几里得距离定义为d(p′, p′′) = √(∑(s = 1到k)(x′s – x′′s)²)。k维最近点对问题的蛮力算法的时间效率类别是什么?
该蛮力算法的时间效率类别为Θ(n²),因为都需要计算每对不同点之间的距离,基本操作次数的数量级不受维度
k
的影响。
169、求以下集合的凸包并确定它们的极点(如果有的话):一段曲线。
曲线段的凸包是连接该曲线段两个端点的线段,其极点是曲线段的两个端点。
170、找出以下集合的凸包并确定其极值点(如果有的话):一个圆
圆的凸包是其自身,极值点是圆周上的所有点。
171、求下列集合的凸包并确定其极点(如果有的话):六边形的边界
该集合的凸包是由六边形边界所围成的区域,其极点是六边形的六个顶点。
172、求以下集合的凸包并确定其极点(如果有的话):一个五边形
五边形的凸包是其自身,极点是五边形的五个顶点。
173、考虑以下用于检查由邻接矩阵定义的图的连通性的算法。算法如下:Connected(A[0..n – 1, 0..n – 1]),输入为无向图G的邻接矩阵A[0..n – 1, 0..n – 1],输出为若G连通则为1(真),否则为0(假)。若n = 1则返回1(根据定义,单顶点图是连通的),否则若Connected(A[0..n – 2, 0..n – 2])返回0则返回0,否则对j从0到n – 2进行循环,若A[n – 1, j]则返回1,最后返回0。该算法是否能对每个具有n > 0个顶点的无向图正确工作?如果回答是,请指出该算法在最坏情况下的效率等级;如果回答否,请解释原因。
该算法不能对每个具有
n > 0
个顶点的无向图正确工作。
该算法的递归部分仅检查去掉最后一个顶点后的子图是否连通,然后仅检查最后一个顶点是否与子图中的任何顶点相连。
但这不能保证整个图是连通的,因为可能存在
子图内部不连通
且
最后一个顶点仅与子图的一部分相连
的情况。
174、数组A[0..n – 2]按升序包含了从1到n的n – 1个整数。(因此这个范围内有一个整数缺失)。请设计一个你能想到的最有效的算法来找出缺失的整数,并指出其时间效率。
可以使用等差数列求和公式来解决这个问题。首先,计算从1到n的所有整数的和,公式为:
sumn=n×(n+1)/2sumn=n×(n+1)/2
然后,遍历数组A,计算数组中所有元素的和 $ ext{sum}_A$。
最后,缺失的整数即为:
sumn−sumAsumn−sumA
该算法的时间效率为 $O(n)$,因为只需要遍历一次数组来计算 $ ext{sum}_A$。
175、使用归并排序对列表 S, O, R, T, I, N, G 按字母顺序进行排序。
首先将列表分成两部分,分别对每部分递归排序,再将排序好的两部分合并。具体步骤如下:
初始列表:
S, O, R, T, I, N, G
第一次划分:
[S, O, R, T]
和
[I, N, G]
对
[S, O, R, T]
划分:
[S, O]
和
[R, T]
,对
[I, N, G]
划分:
[I, N]
和
[G]
对
[S, O]
排序得
[O, S]
,对
[R, T]
排序得
[R, T]
,对
[I, N]
排序得
[I, N]
合并
[O, S]
和
[R, T]
得
[O, R, S, T]
,合并
[I, N]
和
[G]
得
[G, I, N]
最后合并
[O, R, S, T]
和
[G, I, N]
得到最终排序结果
[G, I, N, O, R, S, T]
176、判断对错:对于每一个 n > 1,都存在 n 个元素的数组,插入排序对其排序的速度比快速排序快。
正确
177、考虑二维邮局选址问题:给定笛卡尔平面上的n个点(x1, y1),…, (xn, yn),找到一个邮局的位置(x, y),使1/n ∑i = 1n(|xi – x| + |yi – y|)最小,即邮局到这些点的平均曼哈顿距离最小。假设邮局不必位于输入的点之一,说明如何通过问题归约技术有效地解决这个问题。
可以先求解该问题的一维版本(即分别考虑 x 坐标和 y 坐标),再将二维问题归约为两个一维问题。分别找出使
∑i=1n|xi−x|∑i=1n|xi−x|
最小的 x 值和使
∑i=1n|yi−y|∑i=1n|yi−y|
最小的 y 值,这两个值组合起来的 (x, y) 即为二维问题中使平均曼哈顿距离最小的邮局位置。
178、考虑使用霍斯普尔算法在DNA序列中搜索基因的问题。DNA序列用字母表{A, C, G, T}上的文本表示,基因或基因片段为模式串。为10号染色体的以下基因片段构建移位表:TCCTATTCTT
根据霍斯普尔算法构建移位表的规则,需统计模式串中每个字符最后一次出现位置到模式串末尾的距离。
模式串“TCCTATTCTT”长度为10。
字符 T:
最后一次出现在第 10 位,距离末尾为 0。
但在移位表中应考虑其前一次出现位置,即第 8 位,距离末尾为 2。
字符 C:
最后一次出现在第 9 位,距离末尾为 1。
字符 A:
最后一次出现在第 6 位,距离末尾为 4。
未在模式串中出现的字符:
移位值为模式串长度 10。
所以移位表为:
字符 A 的移位值 $ t(A) = 4 $
字符 C 的移位值 $ t(C) = 1 $
字符 G 的移位值 $ t(G) = 10 $
字符 T 的移位值 $ t(T) = 2 $
179、在“杰克吸管”游戏中,一些塑料或木制的“吸管”被倒在桌子上,玩家试图一根一根地移除它们,同时不干扰其他吸管。在这里,我们只关心不同的吸管对是否通过相互接触的吸管路径相连。给定n(n > 1)根吸管的端点列表(就好像它们被倒在一张大坐标纸上),确定所有相连的吸管对。请注意,接触即相连,并且两根吸管也可以通过其他相连的吸管间接相连。[1994年ACM国际大学生程序设计竞赛东中部地区赛]
可使用图论相关算法来解决此问题。将每根吸管看作图中的一个顶点,若两根吸管接触,则在对应的两个顶点之间连一条边。可以使用深度优先搜索(DFS)或广度优先搜索(BFS)算法遍历图,找出所有相连的顶点对,即相连的吸管对。
180、编写找零问题贪心算法的伪代码,输入为金额n和硬币面额d1 > d2 >… > dm。该算法的时间复杂度类别是什么?
# 贪心算法解决找零问题
## 伪代码如下:
ALGORITHM GreedyChangeMaking(D[1..m], n)
// 使用贪心算法解决找零问题
// 输入:正整数n和按降序排列的正整数数组D[1..m],表示硬币面额
// 输出:找零所需的最少硬币数量
coins ← 0
for i ← 1 to m do
while n ≥ D[i] do
n ← n – D[i]
coins ← coins + 1
return coins
## 时间复杂度分析
该算法的时间复杂度为 **O(m)**,其中 **m** 是硬币面额的数量。因为算法只对每个面额进行一次遍历,且每次遍历中执行的操作是常数时间的。
181、判断以下陈述的真假:a. 如果 e 是一个连通加权图中权重最小的边,那么它一定是该图至少一棵最小生成树的边。b. 如果 e 是一个连通加权图中权重最小的边,那么它一定是该图每一棵最小生成树的边。c. 如果一个连通加权图的所有边的权重都不同,那么该图一定有且仅有一棵最小生成树。d. 如果一个连通加权图的边权重并非都不同,那么该图一定有不止一棵最小生成树。
a. 真;b. 假;c. 真;d. 假
182、证明快速合并的按大小合并(union – by – size)版本中,查找操作find(x)的时间复杂度为O(log n)。
在按大小合并的快速合并中,每次合并操作总是将较小的树连接到较大的树的根上。设集合
S
有
n
个元素,对于集合
S
中的元素
a
i
,其代表元更新的次数记为
A
i
。
每次
a
i
的代表元更新时,
a
i
所在的子集必须是参与合并的较小子集,且合并后集合的大小至少是包含
a
i
的子集大小的两倍。
因此,当
a
i
的代表元第一次更新时,结果集至少有两个元素;第二次更新时,结果集至少有四个元素;一般地,如果更新了
A
i
次,结果集至少有 2
A
i
个元素。
由于整个集合
S
有
n
个元素,所以
2Ai≤n2Ai≤n
即
Ai≤log2nAi≤log2n
对于查找操作
find(x)
,需要从包含
x
的节点沿着指针链找到树的根。因为每次合并都保证了树的大小增长规律,使得树的高度是对数级别的,所以每次查找操作可以在
O(log n)
时间内完成。
183、请给出以下算法的一个实际生活应用:a. 普里姆算法 b. 克鲁斯卡尔算法
普里姆算法可用于城市之间通信网络的建设,以最小成本连接所有城市。
克鲁斯卡尔算法可用于规划铁路线路,在连接多个站点时使总成本最小。
184、某个问题可以由一个运行时间为O(nlog₂n)的算法解决。以下哪个断言是正确的?a. 该问题是可处理的。b. 该问题是不可处理的。c. 无法判断。
a
185、亚瑟王预计有150名骑士参加在卡美洛举行的年度晚宴。不幸的是,一些骑士之间会发生争吵,亚瑟王知道哪些骑士之间会争吵。亚瑟王想让他的客人围坐在一张桌子旁,这样就不会有两个争吵的骑士坐在一起。a. 可以用哪个标准问题来建模亚瑟王的任务?b. 作为一个研究项目,找出一个证明,即如果每个骑士至少与75个其他骑士不争吵,那么亚瑟王的问题有解。
a. 可以用图的哈密顿回路问题来建模,将骑士看作图的顶点,不争吵的骑士之间有边相连,问题就转化为在图中找到一个哈密顿回路。
b. 需自行进行研究寻找证明。
186、a. 继续四皇后问题的回溯搜索,以找到该问题的第二个解。b. 解释如何利用棋盘的对称性来找到四皇后问题的第二个解。
a. 已知第一个解是
– 皇后1在(1, 2)
– 皇后2在(2, 4)
– 皇后3在(3, 1)
– 皇后4在(4, 3)
算法可从该解对应的叶子节点继续回溯搜索。先回溯到皇后4,尝试其他列位置,若不行则回溯到皇后3,依次类推,继续寻找下一个可行解。
b. 棋盘具有对称性,如
– 水平对称
– 垂直对称
– 旋转对称(90度、180度、270度等)
对于四皇后问题的一个解,可通过对棋盘进行对称变换(如水平翻转、垂直翻转、旋转90度、180度、270度等)得到其他可能的解。例如将第一个解的棋盘进行某种对称变换后,可得到第二个解。
187、a. 用你选择的编程语言实现 n 皇后问题的回溯算法。对一组 n 的值运行你的程序,以获得该算法状态空间树中的节点数量。将这些数量与该问题的穷举搜索算法生成的候选解数量进行比较。b. 对于你在 (a) 部分运行程序的每个 n 值,估计状态空间树的大小,并将估计值与你获得的实际节点数量进行比较。
对于a部分,可利用回溯算法通用模板,需判断给定皇后放置中是否有两个皇后相互攻击。为便于与穷举搜索算法比较,可考虑不利用棋盘对称性找出问题所有解的版本,穷举搜索算法可尝试n个皇后在n×n棋盘的n个不同方格上的所有放置,或仅考虑皇后在不同行的放置,或仅考虑在不同行和不同列的放置。
对于b部分,为得到树大小的合理准确估计,需计算多个随机路径的平均值。
188、用你选择的一种编程语言实现对一组n个键的最优二叉搜索树进行元素搜索。针对搜索最小元素这种情况测试代码的时间效率。
在最优二叉搜索树中,最小元素位于最左侧节点。可以从根节点开始,不断访问左子节点,直到左子节点为空,此时的节点即为最小元素。时间复杂度为
O(h)
,h 为树的高度。
以下是 Python 示例代码:
class TreeNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def find_min(root):
current = root
while current.left is not None:
current = current.left
return current.key
你可以根据实际情况创建最优二叉搜索树并调用
find_min
函数进行测试。
189、求解具有如下权重矩阵的有向图的所有顶点对最短路径问题:
(
(03∞26 5042∞ ∞∞05∞ ∞∞104 5∞∞∞0)(03∞26 5042∞ ∞∞05∞ ∞∞104 5∞∞∞0))
可使用Floyd算法来解决该问题。算法步骤为:
初始化距离矩阵 $ D $ 等于权重矩阵。
通过三重循环,对于每一个中间顶点 $ k $,检查所有顶点对 $ (i, j) $。
如果通过顶点 $ k $ 使得顶点 $ i $ 到顶点 $ j $ 的距离更短,则更新距离矩阵 $ D $ 中对应元素的值。
但具体计算过程需手动或借助工具按照算法步骤完成。