欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
- 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
- 希望系统学习C++/Python编程的初学者
- 想要提升算法与编程能力的编程爱好者
参考链接
https://mp.weixin.qq.com/s/bsYJCcOWAk477nJGhGhuew
单项选择题
第1题
32位unsigned int 类型的存储范围是( )。
A.0~2147483648
B.0~4294967295
C.-2147483648~2147483647
D.-4294967296~4294967295
【答案】:B
【解析】
解析在C++中,unsigned int 表示范围为 0 ∼ 2 32 − 1 0\sim 2^{32}-1 0∼232−1
第2题
计算 2025 16 − 2024 10 + 2023 8 − 1011 2 ∗ 2 16 2025_{16}-2024_{10}+2023_{8}-1011_{2}*2_{16} 202516−202410+20238−10112∗216 的十六进制计算结果( )。
A.1B3A
B.3B1A
C.1C3A
D.3C1A
【答案】:C
【解析】
将题目数据转换为十进制计算后的结果为:8229-2024+1043-11*2=7226,然后将7226转换为十六进制的结果为1C3A
第3题
8人排成一排,其中甲和乙不能相邻,丙、丁、戊之间互不相邻,一共有( )种站法。
A.2880
B.5640
C.8640
D.11520
【答案】:D
【解析】
先不考虑甲乙,使用插空法放置丙丁戊。共有5人形成6个空位,方案数为, A 5 5 C 6 3 A 3 3 A_5^5C_6^3A_3^3 A55C63A33
再考虑甲乙相邻,将甲乙捆绑,然后插空放置丙丁戊,共有4人形成5个空位: A 4 4 A 2 2 C 5 3 A 3 3 A_4^4A_2^2C_5^3A_3^3 A44A22C53A33
最终方案数为上述两个方案相减,结果为D
第4题
已知字符集{A,B,C,D,E}的出现频率分别为10%、15%、20%、25%、30%。使用哈夫曼编码时,以下哪项描述正确?
A.字符A的编码长度为2位,字符D的编码长度为3位
B.字符B的编码长度为3位,字符E的编码长度为1位
C.字符C的编码长度为1位,字符D的编码长度为2位
D.字符A的编码长度为3位,字符E的编码长度为2位
【答案】:D
【解析】
哈夫曼编码的规则是频率越高的字符编码越短。通过合并最小频率逐步构建哈夫曼树,可得:
A(10%)和B(15%)首先合并(总25%),再与C(20%)合并(总45%),最后与合并后的D(25%)+E(30%)(总55%)合并形成完整树。字符A的编码路径最长(需3位),而高频字符D和E仅需2位。错误选项中,C选项混淆了字符C的编码长度(实际为2位),B选项误判E的编码长度为1位,而A选项错误分配了D的编码长度。
第5题
已知根节点深度为1的完全二叉树有2025个结点,则该二叉树中的叶子结点数目为?
A.1002
B.512
C.1013
D.1024
【答案】:C
【解析】
最后一层叶子数:总节点数-前10层满二叉树节点数 ( 2 10 − 1 ) = 1023 (2^{10}-1)=1023 (210−1)=1023,得2025-1023=1002
倒数第二层叶子数:第10层共 2 9 = 512 2^9=512 29=512个节点,其中前 ⌈ 1002 / 2 ⌉ = 501 \lceil 1002/2\rceil=501 ⌈1002/2⌉=501个节点有子节点,剩余512-501=11个是叶子结点。
第6题
下列不是C++中关键字(保留字)的是( )。
A.cin
B.this
C.do
D.try
【答案】:A
【解析】
cin 是输入流函数,不是关键字
第7题
后缀表达式7 8 9 - 2 * + 3 / 对应的正确中缀表达式是?
A.7 + 8 - 9 * 2 / 3
B.(7 + (8 - 9) * 2) / 3
C.7 + ((8-9) * 2 / 3)
D.((7 + 8 - 9) * 2) / 3
【答案】:B
【解析】
使用栈模拟即可,7 8 9依次入栈,然后是-,8 9出栈计算 8-9 并入栈,然后是 2 入栈,接下来读到* , 计算 (8-9) * 2 并入栈,接下来读到+,计算7+(8-9) * 2并入栈,最后计算(7+(8-9) * 2)/3结果选择B
第8题
在单链表中,已知指针p指向要删除的节点。正确的删除操作伪代码应为:(其中next表示节点的后继,head表示链表表头)
A.delete§; p->next = p->next->next;
B.prev = head; while(prev != p) prev=prev->next; prev->next = p->next; delete§;
C.prev->next = p->next; delete§;
D.q = p->next; p->next = q->next; delete(q);
【答案】:B
【解析】
想要删除p且维护链表的正确性,需要先找到p的前驱,然后将p前驱的next指向p的next。由于是单向链表,找前驱需要使用循环。
第9题
一张分辨率为 1280×720 的 32 位真彩色未压缩位图,占用的存储空间约为?
A.3.52MB
B.29.5MB
C.11.25MB
D.7.03MB
【答案】:A
【解析】
需要转换为字节计算,8位为一个字节,1280×720×32bit +8+1024+1024=3.515625MB
第10题
下列有关快速排序算法中说法正确的是:
A.平均时间复杂度为O(n³)
B.属于稳定排序算法
C.最坏时间复杂度与初始数据排列有关
D.空间复杂度始终为O(1)
【答案】:C
【解析】
A:平均复杂度为O(nlogn)
B:快速排序不稳定
C:当原序列有序时快速排序会到达最坏复杂度
D:由于有排序的数组,空间复杂度为O(n)
第11题
对于使用二分法,下列说法不正确的是( )。
A.二分法要求序列必须有序
B.二分法查询元素时序列中可以包含重复元素
C.二分法的时间复杂度为 O ( n ) O(\sqrt n) O(n)
D.二分法不能用于链表上的元素查询
【答案】:C
【解析】
A:二分法必须需要在有序的序列上才能进行
B:二分法基于比较决定二分的区间,可以包含重复元素
C:二分法的复杂度为 O(logn)
D:链表不支持随机访问,二分法需要随机访问中点
第12题
下列选项中不是下图的一个拓扑排序的是( )。
A.1 2 3 4 5
B.2 1 4 3 5
C.2 3 1 4 5
D.2 3 4 1 5
【答案】:D
【解析】
根据拓扑排序定义,每次需要选择入度为0的节点,在删去2 3之后,1为入度为0的点,D错误。
第13题
已知二叉树的前序遍历为 A,B,D,E,C,F,中序遍历为D,B,E,A,C,F,则后序遍历是:
A.D,E,B,F,C,A
B.D,E,B,C,F,A
C.D,B,E,F,C,A
D. D,B,E,C,F,A
【答案】:A
【解析】
根据遍历顺序不难建出二叉树:
第14题
将6本不同的书分给甲、乙、丙三人,要求每人至少得到1本,共有多少种分法?
A.537
B.540
C.720
D.729
【答案】:B
【解析】
总分配方式:每本书有3种选择(甲/乙/丙),共3^6=729种。排除非法情况:需减去"至少一人未得到书”的情形。一人未得:选择未得书的人(3种),剩余书分给另外两人,即3×2^6=192种。两人未得:需加回多减的"两人未得"情形,即3×1^6=3种。
最终结果:3^6-3×2^6+3×1^6=729-192+3=540
第15题
完整的计算机系统由( )组成。
A.运算器、控制器、存储器、输入设备和输出设备
B.主机和外部设备
C.硬件系统和软件系统
D.主机箱、显示器、键盘、鼠标、打印机
【答案】:C
【解析】
完整的计算机系统由硬件系统和软件系统组成。
阅读程序
第16题
已知输入都是正整数、且 a i a_i ai 为 1 ∼ n 1\sim n 1∼n 的一个排列。回答下列问题:
#include<iostream>
using namespace std;
int q, n, a[10005];
int stk[10005];
string chk(){
stk[0] = 0;
int t = 1;
for (int i=1; i<=n; i++) {
if (a[i] >= t) {
while (t <= a[i]) stk[++stk[0]] = t++;
--stk[0];
}
else {
if (a[i] == stk[stk[0]]) --stk[0];
else return "No\n";
}
}
return "Yes\n";
}
int main(){
cin >> n;
for (int i=1; i<=n; i++) cin >> a[i];
cout << chk();
return 0;
}
第16题
代码中使用了“栈”的数据结构( )。
A.正确
B.错误
【答案】:A
【解析】
本题代码用于验证当入栈序列为 1 ∼ n 1\sim n 1∼n时出栈序列是否合法,其中stk数组使用数组模拟栈。stk[0]作为栈顶。该选项正确。
第17题
若 a 数组为{5,4,3,2,1},则输出为No( )。
A.正确
B.错误
【答案】:B
【解析】
本题代码用于验证当入栈序列为 1 ∼ n 1\sim n 1∼n时出栈序列是否合法,显然5,4,3,2,1是一个合法的栈序列,输出Yes
第18题
将第10行的"t++" 改为"++t" 不会影响输入结果( )。
A.正确
B.错误
【答案】:B
【解析】
本题代码用于验证当入栈序列为 1 ∼ n 1\sim n 1∼n时出栈序列是否合法,其中第10行的含义为当我们发现当前出栈元素比栈中出现过的最大的元素还要大的话,那他们之间的所有元素一定会在栈中。将这些元素入栈。由于t初始值为1,改为++t会导致1无法入栈,答案错误。
第19题
上述代码的时间复杂度为( )。
A. O ( n ) O(n) O(n)
B. O ( n l o g n ) O(nlogn) O(nlogn)
C. O ( n 2 ) O(n^2) O(n2)
D. O ( 2 n ) O(2^n) O(2n)
【答案】:A
【解析】
虽然代码中包含了循环嵌套,但是实际上每个元素只入栈/出栈过一次,因此时间复杂度为O(n)
第20题
已知n=7,使得程序输出结果为"No"的 a 数组为以下的( )。
A.3,2,1,7,6,5,4
B.3,5,4,7,6,1,2
C.3,5,4,7,6,2,1
D.7,6,5,4,3,2,1
【答案】:B
【解析】
该代码为验证栈序列是否合法,输出No则栈序列不合法,B选项中最后两项,此时栈中元素为12,先出栈的元素只能为2,B不合法。
已知输入均为正整数,回答下列问题:
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int calc(vector<int> &a, int S) {
int n = a.size() - 1;
vector<int> dp(n+1, 0);
dp[0] = 1;
for (int i=1; i<=n; i++)
for (int j=1; j<=i; j++)
if (a[i]-a[i-j] <= S)
dp[i] = (dp[i] + dp[i-j]) % mod;
return dp[n];
}
int main(){
int n, S;
cin >> n >> S;
vector<int> a(n+1, 0);
for (int i=1; i<=n; i++) {
cin >> a[i];
a[i] += a[i-1];
}
cout << calc(a, S) << endl;
return 0;
}
第21题
当S=10,a数组为{10,5,3}日时输出结果为2( )。
A.正确
B.错误
【答案】:A
【解析】
原代码用于求解将一个序列分为若干份,每一份的总和不超过S的方案数,使用动态规划完成。由于数据量很小,可以手动计算。需要先计算出前缀和数组10,15,18,然后根据前缀和数组推出dp数组的值,dp[n]=2,正确
第22题
将第19行的"+1"删去会导致编译错误( )。
A.正确
B.错误
【答案】:B
【解析】
删去+1,会导致vector下标只能到n-1,不会导致编译错误,但是会导致运行错误。
第23题
如果S的值小于输入的a数组中的最小值,那么输出将会恒为0( )。
A.正确
B.错误
【答案】:A
【解析】
由11行可以知道,dp数组需要更新就必须要满足a数组中的一个区间和不大于S才可以更新,因此当S比a数组中的最小值小的时候,dp值无法更新,除了dp[0]之外全都是0。
第24题
若S=5,a数组为{1,2,3,4,5},输出结果为( )。
A.1
B.2
C.3
D.4
【答案】:C
【解析】
原代码用于求解将一个序列分为若干份,每一份的总和不超过S的方案数,则原序列可以分为{|1|2|3|4|5|},{|1,2|3|4|5|},{|1|, |2,3|, |4|, |5|}
第25题
若S=15,a数组为{1,2,3,4,5},输出结果为( )。
A.4
B.8
C.16
D.32
【答案】:C
【解析】
原代码用于求解将一个序列分为若干份,每一份的总和不超过S的方案数,不难发现总和不超过S,所以可以随便划分,使用插空法计算,将n个元素划分为若干份的方案数为 2 n − 1 = 2 4 = 16 2^{n-1}=2^4=16 2n−1=24=16
第26题
若S=2,a数组所有元素均为1,则当n=15时输出结果为( )。
A.978
B.610
C.987
D.620
【答案】:C
【解析】
当S=2,a数组中元素为1时,当且仅当j=1和2时11行条件满足,也就是说dp[i]=dp[i-1]+dp[i-2],可以发现dp数组是一个斐波那契数列,dp[0]=F[1],那么dp[15]=F[16],斐波那契第16项为987
#include<bits/stdc++.h>
using namespace std;
int solve1(int a, int b){
if (b==0) return a;
if (a<b) {
a ^= b;
b ^= a;
a ^= b;
}
return solve1(b, a%b);
}
int solve2(int a, int b) {
while (b) {
int t = a%b;
a = b;
b = t;
}
return a;
}
int main() {
int a, b;
cin >> a >> b;
cout << solve1(a, b) << endl;
cout << solve2(a, b) << endl;
return 0;
}
第27题
若输入均为正整数,删去代码的6~10行会对代码输出结果有影响( )。
A.正确
B.错误
【答案】:B
【解析】
本题为求解两数的最大公约数,其中 6 ∼ 10 6\sim 10 6∼10行为交换操作,使用异或实现,由于辗转相除的时候里面蕴含一个交换在里面,所以删去也没有影响。
第28题
若输入为任意非零整数,该程序可能会进入无限递归( )。
A.正确
B.错误
【答案】:A
【解析】
gcd(b,a%b)中的交换会将绝对值大的数放在左边,但是如果出现负数的绝对值较大的情况,会在前面的交换之后再交换一次,导致递归无法结束。
第29题
若输入均为正整数,输出的两行一定相同( )。
A.正确
B.错误
【答案】:A
【解析】
两个函数都是辗转相除法,solve1使用递归实现,solve2使用循环实现,结果相同
第30题
输入a=15,b=12,则solve2(15, 12) 的返回值为( )。
A.1
B.3
C.5
D.12
【答案】:B
【解析】
求解15和12的最大公约数,为3
第31题
输入a=45,b=81,则solve1(45, 81) 的返回值为( )。
A.3
B.6
C.9
D.18
【答案】:C
【解析】
计算45和81的最大公约数,结果为9
第32题
设a,b同阶,则该程序的时间复杂度为( )。
A. O ( a + b ) O(a+b) O(a+b)
B. O ( a / b ) O(a/b) O(a/b)
C. O ( l o g ( m a x ( a , b ) ) ) O(log(max(a,b))) O(log(max(a,b)))
D. O ( b l o g a ) O(bloga) O(bloga)
【答案】:C
【解析】
辗转相除法的时间复杂度为对数级别,选择C选项
完善程序
区间修改问题:
现有一个长度为 n n n 的序列,需要对其执行若干次操作,每次操作会将序列的一个区间中的所有数都加上一个值,求所有操作结束后序列中各个元素的值。
使用差分完成该试题,试补全以下程序。
#include<iostream>
using namespace std;
int a[1000], d[1000];
int main()
{
int n, m;
cin >> n;
for (int i=1; i<=n; i++)
{
cin >> a[i];
__1__;
}
cin >> m;
while (m--)
{
int l, r, x;
cin >> l >> r >> x;
d[l] = __2__;
d[__3__] -= x;
}
for (int i=1; i<=n; i++)
{
__4__;
cout << __5__ << endl;
}
return 0;
}
第33题
1处该填写:
A.d[i] = a[i] - a[i-1]
B.d[i] = a[i+1] - a[i]
C.a[i] -= a[i-1]
D.a[i+1] -= a[i]
【答案】:A
【解析】
本题使用差分法解决区间修改单点查询问题,需要先生成一个差分数组,选择A选项
第34题
2处该填写:
A.a[l] - x
B.a[l] + x
C.d[l] + x
D.d[l] - x
【答案】:C
【解析】
本题使用差分法解决区间修改单点查询问题,在区间修改中,我们需要将差分数组的左端点加上值,在右端点的下一个点减去值,选择C选项
第35题
3处该填写:
A.r
B.r + 1
C.l
D.l + 1
【答案】:B
【解析】
本题使用差分法解决区间修改单点查询问题,在区间修改中,我们需要将差分数组的左端点加上值,在右端点的下一个点减去值,选择B选项
第36题
4处该填写:
A.d[i] += a[i-1]
B.a[i] = d[i] + d[i-1]
C.a[i] += a[i-1]
D.d[i] += d[i-1]
【答案】:D
【解析】
本题使用差分法解决区间修改单点查询问题,在单点查询中,我们需要先计算出差分数组的前缀和,然后该前缀和数组就是修改后的原数组,选择D选项
第37题
5处该填写:
A.d[i]
B.a[i]
C.a[i] + d[i]
D.a[i] - d[i]
【答案】:A
【解析】
本题使用差分法解决区间修改单点查询问题,在单点查询中,我们需要先计算出差分数组的前缀和,然后该前缀和数组就是修改后的原数组,选择A选项
矩阵翻转
给定一个 n × n n×n n×n 的矩阵,我们需要对它进行若干次翻转操作,最后输出翻转后的结果。
其中执行的翻转操作为四种:1.上下翻转。2.左右翻转。3.顺时针翻转90度。4.逆时针翻转90度。
完善下列程序
#include<bits/stdc++.h>
using namespace std;
int n, m, k;
int a[101][101], b[101][101], c[101][101];
void R90(){
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
c[i][j] = __1__;
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
b[i][j] = c[i][j];
}
int main(){
cin >> n >> k;
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
cin >> a[i][j];
int f = 1, r = 0;
while (k--) {
int op;
cin >> op;
if (__2__) f = -f;
if (op == 1) r = (6-r) % 4;
if (op == 2) __3__;
if (op == 3) r = (r + 1) % 4;
if (op == 4) r = (r + 3) % 4;
}
if (f < 0)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
b[i][j] = a[i][__4__];
else
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
b[i][j] = __5__;
for (int t=1; t<=r; t++) R90();
for (int i=1; i<=n; i++) {
for (int j=1; j<=n; j++)
cout << c[i][j] << " ";
cout << endl;
}
}
第38题
1处该填写:
A.b[j][n-i+1]
B.b[n+1-i][j]
C.b[n+1-j][i]
D.b[i][n-j+1]
【答案】:C
【解析】
根据函数名称判断,这里是向右也就是顺时针翻转90度,选择C。或者带入特殊数据,设k=1,op=3时r=1。则只需要顺时针翻转90度一次,执行一次顺时针翻转也是C选项。
2处该填写:
A.f == -1
B.op <= 2
C.op > 2
D.f == 1
【答案】:B
【解析】
这里f变量的正负用来表示当前数组是否处于镜像翻转的状态,而只有前两项涉及了镜像翻转,所以op<=2,选择B选项。
3处该填写:
A.r=(1-r)%4
B.r=(2-r)%4
C.r=(3-r)%4
D.r=(4-r)%4
【答案】:D
【解析】
左右镜面翻转之后,如果原数组向右翻转了一次,则在镜像下等于向左翻转了一次也就是向右翻转了3次。其实不难发现当一个图翻转后,他向右翻转的次数将会变为与4互补的个数,所以是4-r。选择D选项
4处该填写:
A.n-j+1
B.j
C.n-i+1
D.i
【答案】:A
【解析】
当f为负数时,需要执行左右镜面翻转,选择A选项。
5处该填写:
A.a[n-i+1][j]
B.a[i][n-j+1]
C.a[j][i]
D.a[i][j]
【答案】:D
【解析】
当f不变时候,就是原数组上进行操作。可以带入k=0的特殊数据,不难得到选择D选项。