CSP-S 2025 初赛题目解析
单项选择
T1
有 \(5\) 个红色球和 \(5\) 个蓝色球,它们除了颜色之外完全相同。将这 \(10\) 个球排成一排,要求任意两个蓝色球都不能相邻,有多少种不同的排列方法?
- A. 25
- B. 30
- C. 6
- D. 120
答案
C
将 \(5\) 个红色球排成一排,共有 \(6\) 个空隙(_R_R_R_R_R_)。
将 \(5\) 个蓝色球,放在 \(6\) 个空中,共 \(\binom{6}{5} = 6\)
T2
在 KMP 算法中,对于模式串 \(P = \text{abacaba}\),其 next 数组( \(next[i]\) 定义为模式串 \(P[0\dots i]\) 最长公共前后缀的长度,且数组下标从 \(0\) 开始)的值是什么?
- A. {0,0,1,0,1,2,3}
- B. {0,1,2,3,4,5,6}
- C. {0,0,1,1,2,2,3}
- D. {0,0,0,0,1,2,3}
答案
A
模拟即可。
T3
对一个大小为 \(16\)(下标 \(0 \sim 15\))的数组上构建满线段树。查询区间 \([3,11]\) 时,最少需要访问多少个树结点(包括路径上的父结点和完全包含在查询区间内的结点)?
- A. 7
- B. 8
- C. 9
- D. 10
答案
B
graph TD;
A[0,15] --> B[0,7];
A --> C[8,15];
B --> D[0,3];
B --> E[4,7];
C --> F[8,11];
C --> G[12,15];
D --> H[0,1];
D --> I[2,3];
I --> J[2];
I --> K[3];
style A fill:red;
style B fill:red;
style C fill:red;
style D fill:red;
style E fill:red;
style F fill:red;
style I fill:red;
style K fill:red;
T4
将字符串 cat, car, cart, case, dog, do 插入一个空的 Trie 树(前缀树)中。构建完成的 Trie 树(包括根结点)共有多少个结点?
- A. 8
- B. 9
- C. 10
- D. 11
答案
D
graph TD;
rt[root] --> c0[c];
c0 --> a1[a];
a1 --> t2[t];
a1 --> r2[r];
r2 --> t3[t];
a1 --> s2[s];
s2 --> e3[e];
rt --> d0[d];
d0 --> o1[o];
o1 --> g2[g];
T5
对于一个包含 \(n\) 个顶点和 \(m\) 条边的有向无环图(DAG),其拓扑排序的结果有多少种可能?
- A. 只有 \(1\) 种
- B. 最多 \(n\) 种
- C. 等于 \(n-m\) 种
- D. 以上都不对
答案
D
拓扑排序的数量取决于图的具体结构,没有一个简单的公式可以计算。
如,没有边为 \(n!\) 种,是链则只有 \(1\) 种。
T6
在一个大小为 \(13\) 的哈希表中,使用闭散列法的线性探查来解决冲突。哈希函数为 \(H(key)=key \mod 13\)。依次插入关键字 18, 26, 35, 9, 68, 74。插入 74 后,它最终被放置在哪个索引位置?
- A. 5
- B. 7
- C. 9
- D. 11
答案
D
- \(18 \mod 13 = 5\)
- \(26 \mod 13 = 0\)
- \(35 \mod 13 = 9\)
- \(9 \mod 13 = 9\),\(9\) 冲突,放在第 \(10\) 个位置
- \(68 \mod 13 = 3\)
- \(74 \mod 13 = 9\),\(9,10\) 冲突,放在第 \(11\) 个位置
T7
一个包含 \(8\) 个顶点的完全图(顶点的编号为 \(1\) 到 \(8\)),任意两点之间的边权重等于两顶点编号的差的绝对值。例如,顶点 \(3\) 和 \(7\) 之间的边权重为 \(|7 − 3| = 4\)。该图的最小生成树的总权重是多少?
- A. 7
- B. 8
- C. 9
- D. 10
答案
A
用 Kruskal 算法求解:
边权最小分别为 \(1\rightarrow 2=1,2\rightarrow3=1,3\rightarrow4=1,4\rightarrow5=1,5\rightarrow6=1,6\rightarrow7=1\)。
总权重为 \(7\)。
T8
如果一棵二叉搜索树的后序遍历序列是 2, 5, 4, 8, 12, 10, 6,那么该树的前序遍历序列是什么?
- A. 6, 4, 2, 5, 10, 8, 12
- B. 6, 4, 5, 2, 10, 12, 8
- C. 2, 4, 5, 6, 8, 10, 12
- D. 12, 8, 10, 5, 2, 4, 6
答案
A
后序遍历 2, 5, 4, 8, 12, 10, 6,根是最后一个元素 \(6\)。
二叉搜索树保证:左子树 < 根 < 右子树,所以左子树节点 2, 5, 4,右子树 8, 12, 10。
再结合后序遍历,可得:
graph TD;
rt[6] --> A[4];
rt --> B[10];
A --> C[2];
A --> D[5];
B --> E[8];
B --> F[12];
即,前序遍历 6, 4, 2, 5, 10, 8, 12。
T9
一个 0-1 背包问题,背包容量为 \(20\)。现有 \(5\) 个物品,其重量和价值分别为 7,5,4,3,6 和 15,12,9,7,13。装入背包的物品能获得的最大总价值是多少?
- A. 43
- B. 41
- C. 45
- D. 44
答案
D
发现重量小的,价值也小,可以直接枚举。
2, 3, 4, 5,价值 \(41\)。1, 2, 3, 4,价值 \(43\)。1, 3, 4, 5,价值 \(44\)。
T10
在一棵以结点 \(1\) 为根的树中,结点 \(12\) 和结点 \(18\) 的最近公共祖先 (LCA) 是结点 \(4\)。那么下列哪个结点的 LCA 组合是不可能出现的?
- A. LCA(12, 4) = 4
- B. LCA(18, 4) = 4
- C. LCA(12, 18, 4) = 4
- D. LCA(12, 1) = 4
答案
D
\(1\) 是根节点,\(1\) 与任何节点的 LCA 都是 \(1\)。
T11
递归关系式 \(T(n) = 2T(\frac{n}{2}) + O(n^2)\) 描述了某个分治算法的时间复杂度。请问该算法的时间复杂度是多少?
- A. \(O(n)\)
- B. \(O(n\log n)\)
- C. \(O(n^2)\)
- D. \(O(n^2 \log n)\)
答案
C
主定理: \(\log_b a = \log_2 2 = 1\),\(2 > 1\),因此为 \(O(n^2)\)。
T12
在一个初始为空的最小堆(min-heap)中,依次插入元素 20, 12, 15, 8, 10, 5。然后连续执行两次“删除最小值”(delete-min)操作。请问此时堆顶元素是什么?
- A. 10
- B. 12
- C. 15
- D. 20
答案
A
即第三小的值。
T13
\(1\) 到 \(1000\) 之间,不能被 \(2\)、\(3\)、\(5\) 中任意一个数整除的整数有多少个?
- A. 266
- B. 267
- C. 333
- D. 734
答案
A
容斥原理。
- 能被 \(2,3,5\) 整除的数:
- \(2\):\(\lfloor 1000/2 \rfloor = 500\)
- \(3\):\(\lfloor 1000/3 \rfloor = 333\)
- \(5\):\(\lfloor 1000/5 \rfloor = 200\)
- 能被两两相乘整除的数:
- \(2\times 3 = 6\): \(\lfloor 1000/6 \rfloor = 166\)
- \(2\times 5 = 10\): \(\lfloor 1000/10 \rfloor = 100\)
- \(3\times 5 = 15\): \(\lfloor 1000/15 \rfloor = 66\)
- 能被 \(3\) 个数整除的数:
- \(2\times 3\times 5 = 30\): \(\lfloor 1000/30 \rfloor = 33\)
总答案即为 \(1000 - (500+333+200 - 166-100-66+33) = 266\)。
T14
斐波那契数列的定义为 \(F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)\)。使用朴素递归方法计算 \(F(n)\) 的时间复杂度是指数级的。而使用动态规划(或迭代)方法的时间复杂度是线性的。造成这种巨大差异的根本原因是?
- A. 递归函数调用栈开销过大
- B. 操作系统对递归深度有限制
- C. 朴素递归中存在大量的重叠子问题未被重复利用
- D. 动态规划使用了更少的数据存储空间
答案
C
显然,朴素递归中存在大量的重叠子问题未被重复利用,这也是动态规划的优势所在。
T15
有 \(5\) 个独立的、不可抢占的任务 A1, A2, A3, A4, A5 需要在一台机器上执行(从时间 \(0\) 开始执行),每个任务都有对应的处理时长和截止时刻,按顺序分别为 3, 4, 2, 5, 1 和 5, 10, 3, 15, 11。如果某一个任务超时,相应的惩罚等于其处理时长。为了最小化总惩罚,应该优先执行哪个任务?
- A. 处理时间最短的任务
A5 - B. 截止时间最早的任务
A3 - C. 处理时间最长的任务
A4 - D. 任意一个任务都可以
答案
B
贪心模拟即可。
程序阅读
T16
1 |
|
判断题
- 当输入的 \(n=3\) 的时候,程序输出的答案为 \(3\)。
- 在
dfs函数运行过程中,\(k\) 的取值会满足 \(1 \le k \le n+1\)。 - 删除第 \(19\) 行的
flag[i]=false;,对答案不会产生影响。
选择题
- 当输入的 \(n=4\) 的时候, 程序输出的答案为?
- A. 11
- B. 12
- C. 24
- D. 9
- 如果因为某些问题,导致程序运行第 \(25\) 行的
dfs函数之前,数组p的初值并不全为 \(0\),则对程序的影响是?- A. 输出的答案比原答案要小
- B. 无法确定输出的答案
- C. 程序可能陷入死循环
- D. 没有影响
- 假如删去第 \(14\) 行的
if(flag[i]) continue;,输入 \(3\),得到的输出答案是?- A. 27
- B. 3
- C. 16
- D. 12
解析
程序为求解 \(p_i +1 \neq p_{i+1}\) 的长度为 \(n\) 的 排列 \(p\) 的方案数。
答案
TTFADC
判断题
- 手动模拟,得出答案为 \(3\)。
- 显然,第 \(25\) 行,\(k\) 初值为 \(1\);第 \(9 \sim 12\) 行,\(k = n+1\) 就会返回。
- 显然,未清空标记,方案数会减少。
选择题
- 手动模拟。
- 不会影响,因为在计算 \(p_i\) 时,\(p_{i-1}\) 一定 被重新赋值完。
- 删去,则去掉了 排列 的限制,还是手动模拟。
T17
1 |
|
注意:下述的 “猜测数” 为调用 check 函数的次数(即 cnt_check 的值);“猜测正确” 的含义为 assert_ans 函数 return true(执行第 \(25\) 行所在分支)的情况;所有输入保证 \(1 \le k \le n\)。
判断题
- 当输入为
6 5 1时,猜测次数为 \(5\);当输入为6 5 2时,猜测次数为 \(3\)。 - 不管输入的 \(n\) 和 \(k\) 具体为多少,\(t=2\) 时的猜测数总是小于等于 \(t=1\) 时的猜测数。
- 不管 \(t=1\) 或 \(t=2\),程序都一定会猜到正确结果。
选择题
- 函数
guess1在运行过程中,cnt_broken的值最多为?- A. 0
- B. 1
- C. 2
- D. n
- 函数
guess2在运行过程中,最多使用的猜测次数的量级为?- A. \(O(n)\)
- B. \(O(n^2)\)
- C. \(O(\sqrt{n})\)
- D. \(O(\log n)\)
- 当输入的 \(n=100\) 时,代码中 \(t=1\) 和 \(t=2\) 分别需要的猜测次数最多分别为?
- A. 100,14
- B. 100,13
- C. 99,14
- D. 99,13
解析
今年最奇怪的题目
由 egg,cnt_broken 可以得出(?),我们假设这样一个情景:
现在你有两个一样的鸡蛋,你可以进行一种操作:
- 把鸡蛋从一个高度 \(h\) 扔下来,观察它有没有摔碎。如果鸡蛋碎了,这个鸡蛋就不能再用了,你需要用另一个鸡蛋继续尝试,如果两个都碎了(
You have no egg!),则游戏结束。
现在,你想知道会让鸡蛋摔碎的最小高度(guess1),和知道前者的最小操作次数(guess2)。
guess1
guess1 函数比较好懂,就是枚举所有可能的高度,这样会一定会摔碎 \(1\) 个鸡蛋。
guess2
guess2 函数略复杂,它将高度分为了几个区间,我们以 \(n = 15\) 为例,化为了 \([1,5],[6,9],[10,12],[13,14],[15,15]\) 五段,它们的长度依次递减,分别为 \(5,4,3,2,1\)。
对于每段区间,我们先尝试它的右端点,如果没摔碎说明高度足够,就尝试下一个区间。
如果摔碎了,因为只剩一个鸡蛋,只能模仿 guess1 从小到大依次尝试,求出最终答案。
这样摔碎鸡蛋的数量一定是 \(1\) 或 \(2\)。
答案
TFTBCA
判断题
- 手动模拟,得出答案为 \(5\)。
- 不一定,如果鸡蛋在很低的高度就碎了,
guess2需要额外尝试区间的右端点,多一次猜测。 - 显然正确。
选择题
- 显然为 \(1\)。
- 猜测次数的最大值 \(m\),要满足 \(\frac{m(m-1)}{2} \ge n\),解二次不等式得 \(m\) 的最小值为 \(\left\lceil\frac{1+\sqrt{1+8n}}{2}\right\rceil\),量级估算为 \(\sqrt{2n} \approx \sqrt{n}\)。
- 手动模拟。
T18
1 |
|
判断题
- 删除第 \(51\) 行的
std::sort(ans2.begin(), ans2.end());后,代码输出的结果不会受到影响。 - 假设计算过程中不发生溢出,函数
mpow(x, k)的功能是求出 \(x^k\)。 - 代码中第 \(39\) 行到第 \(50\) 行的目的是为了将
ans1数组进行“去重”操作。
选择题
- 当输入为
3 15 1 2 -1 2 1 2时,输出结果为?- A. 4
- B. 8
- C. 0
- D. 10
- 记程序结束前
p数组元素的最大值为 \(P\),则该代码的时间复杂度是?- A. \(O(n)\)
- B. \(O(m^n \log m^n)\)
- C. \(O(m^\frac{n}{2} \log m^\frac{n}{2})\)
- D. \(O(m^\frac{n}{2} (\log m^\frac{n}{2} + \log P))\)
- 本题所求出的是?
- A. 满足 \(a,b,c \in [1,m]\) 的整数方程 \(a^3+b^3=c^3\) 的解的数量
- B. 满足 \(a,b,c \in [1,m]\) 的整数方程 \(a^2+b^2=c^2\) 的解的数量
- C. 满足 \(x_i \in [0,m]\) 的整数方程 \(\sum_{i=1}^n k_i \cdot x_i^{p_i} = 0\) 的解的数量
- D. 满足 \(x_i \in [1,m]\) 的整数方程 \(\sum_{i=1}^n k_i \cdot x_i^{p_i} = 0\) 的解的数量
解析
先看选择题第三问,首先排除 A,B 选项,根本没有出现输入的量。
观察第 \(24\) 行,枚举从 \(1\) 开始,故程序含义为:
- 满足 \(x_i \in [1,m]\) 的整数方程 \(\sum_{i=1}^n k_i \cdot x_i^{p_i} = 0\) 的解的数量。
答案
FTTBDD
判断题
- \(52\) 到 \(59\) 行是双指针计算答案,不排序显然会影响结果。
- 快速幂。
- 显然。
选择题
- 结合程序意义,即求 \(x_1^2+x_3^2=x_2^2,x_i \in [1,15]\) 的方案数,可以手动枚举计算,也容易联想到就是求边长在 \(15\) 以内的直角三角形的数量。
完善程序
T19 特殊最短路
给定一个含 \(N\) 个点、\(M\) 条边的带权无向图,边权非负。起点为 \(S\),终点为 \(T\)。对于一条 \(S\) 到 \(T\) 的路径,可以在整条路径中,至多选择一条边作为“免费边”:当第一次经过这条被选中的边时,费用视为 \(0\);如果之后再次经过该边,则仍按其原始权重计费。点和边均允许重复经过。求从 \(S\) 到 \(T\) 的最小总费用。
以下代码求解了上述问题。试补全程序。
1 |
|
- ① 处应填:
- A. 0
- B. 1
- C. -1
- D. false
- ② 处应填:
- A. d[u][!used]
- B. d[u][used]
- C. d[t][used]
- D. INF
- ③ 处应填:
- A. d[v][1]
- B. d[v][used]
- C. d[u][used]
- D. d[v][0]
- ④ 处应填:
- A. d[v][0]
- B. d[v][1]
- C. d[u][0]
- D. d[u][1]
- ⑤ 处应填:
- A. d[t][1]
- B. d[t][0]
- C. min(d[t][0], d[t][1])
- D. d[t][0]+d[t][1]
T20 最优测试
工厂打算通过客户反馈来间接测试生产线,从而找到存在缺陷的生产线。工厂有 \(n\) 条生产线(编号 \(0\sim n−1\)),已知其中恰有一条生产线存在缺陷。每一轮测试为,从若干生产线的产品取样混合成一个批次发给客户。若该批次中包含缺陷生产线的产品,客户将要求退货(结果记为 \(1\)),否则正常收货(记为 \(0\))。受售后压力限制,在所有发货批次中,最多只能有 \(k\) 次退货(即结果为 \(1\) 的次数 \(\le k\))。工厂的目标是,设计最少的间接测试轮数 \(w\)(发货总批次),保证根据客户收货或退货的反馈结果,唯一确定存在缺陷的生产线。
以下程序实现了工厂的目标,包含两部分:
- 确定 \(w\) 的最小值,并设计最优测试方案;
- 根据测试结果推断存在缺陷的生产线。该程序确定 \(w\) 最小值的方法为:由于不同的生产线故障时,测试应当返回不同的结果,因此 \(w\) 轮测试的可能结果数不应少于生产线数量。
test_subset() 函数为抽象测试接口,输入所有批次的方案并返回一个二进制编码;该编码表示为每批次的检测结果(即最低位是第 \(1\) 批次、最高位是第 \(w\) 批次);其实现在此处未给出。
试补全程序。
1 |
|
- ① 处应填:
- A. (1<<w)<n
- B. count_patterns(w,k) < n
- C. count_patterns(k,w) < n
- D. comb(w,k) < n
- ② 处应填:
- A. next_permutation(bits.begin(), bits.end())
- B. prev_permutation(bits.begin(), bits.end())
- C. next_permutation(bits.begin(), bits.begin()+ones)
- D. prev_permutation(bits.begin(), bits.begin()+ones)
- ③ 处应填:
- A. (j>>i) & 1
- B. (i>>j) & 1
- C. code[i][j] == 1
- D. code[j][i] == 1
- ④ 处应填:
- A. (signature >> i) & 1
- B. (signature >> i) ^ 1
- C. signature | (1<<i)
- D. (signature >> i) | 1
- ⑤ 处应填:
- A. is_permutation(code[j].begin(), code[j].end(), sig_bits.begin())
- B. code[j] == sig_bits
- C. plan[j] == sig_bits
- D. code[j][i] == sig_bits[i]
