2023年12月真题
一、单选题(每题2分,共30分)
正确答案:C
考察知识点:数学问题
解析:本题可抽象为分类计数问题,应使用加法原理,而不是乘法原理。答案为 ACB 的方案数 2 加上 ADB 的方案数 3,结果为5,选 C。
正确答案:A
考察知识点:C++语法
解析:C++在函数中把数组作为参数进行传递时,只会传递数组的首地址,函数中如果想要通过首地址计算数组中任意一位元素所处的地址时,就需要知道第二维数组的长度,比如第二维数组长度为 10 时,a[1][3]的地址就是a[0][0]的地址+13,本题的选项中只有选项 A 给出了数组第二维长度,所以本题选A。
正确答案:D
考察知识点:C++语法
解析:对象的声明周期开始和结束时会分别执行构造函数和析构函数,选项A、B 正确。对于选项 C、D,虚函数是指被 virtual 关键字修饰的成员函数,定义虚函数是为了允许用基类的指针来调用派生类的该函数。允许将析构函数定义为虚函数,是因为有使用“delete 基类指针”来销毁对象的需求,选项C正确。但对象构造时必须指定准确的类,不能使用基类名构造派生类的对象,没有将构造函数定义为虚函数的需要,选项 D 错误。
正确答案:B
考察知识点:图
解析:邻接矩阵的行列均为[0~n-1],所以矩阵的大小为n*n,本题选B。
正确答案:C
考察知识点:排列组合
解析:按照第 1,2,3,4,5 位的顺序依次安排同学,某位同学不能在第一位,所以第 1 位有 4 种安排方法,第二位可以从剩余的 4 名同学中选一位,有4种方法,依次类推,第 3,4,5 各有 3,2,1 种,总方案数为 44321=96,选C。
正确答案:D
考察知识点:图
解析:n 个顶点组成的树包含 n-1 条边,但是题目没有保证图连通,所以可能不存在最小生成树,选 D。
正确答案:A
考察知识点:数学知识
解析:若△ABC 中,已知两边 a.b 和它们的夹角 theta ,作边a 上的高h. 则S=(1/2)ah,而 h/b=sin(theta),即 h=b*sin(theta)
∴S=(1/2)absin(theta)选 A。
正确答案:C
考察知识点:二叉树
解析:树的遍历过程需要对每个元素访问一次,因此时间复杂度为O(n),选择 C。
正确答案:A
考察知识点:算法,时间复杂度
解析:本题代码为辗转相除法,复杂度为 O(logn)。最差情况,输入为斐波那契数列的相邻两项时,循环次数为输入在数列中的位置。选A。
正确答案:C
考察知识点:时间复杂度,快速幂
解析:本题代码为快速幂,复杂度为 O(logn)。通过观察可得该函数的时间复杂度只与 n 相关,假设为 T(n),则 T(n)=T(n / 2) + 常数,求解可得上述时间复杂度。选 C。
正确答案:D
考察知识点:时间复杂度
解析:本题代码是在计算 C[n][m],使用了递归的写法并加上了记忆化搜索,可以通过画图来看所有被访问到的二维数组个数,以 n=6,m=2 为例,可以发现访问的元素个数为 nm-mm,本题选 D。
正确答案:B
考察知识点:图
解析:结构体 edge 里的 next 指向的是下一条边,Node 里的first 指向的是每个点的第一条边,所以 0 号点指向 1 号点,1 号点指向2,3 号点,2 号点指向3号点,3 号点指向 0 号点,选 B。
正确答案:B
考察知识点:多层循环
解析:代码中 a,b,h 的取值范围均为[1,10],要(a+b)*h=20,那么可能的h有1,2,4,5,10,h 为 1 时,(a+b)=20,有 1 种方法,h 为 2 时,(a+b)=10,有9 种方案,依次计算出 h 为 4,5,10 时,(a+b)的方案数依次为 4,3,1,总方案数为1+9+4+3+1=18,选 B。
正确答案:A
考察知识点:多层循环
解析:代码中要求 a,b,c 都是正整数,满足 a+b+c<=30 且,符合要求的a,b,c有 3 4 5;5 12 13;6 8 10 共 3 种,选 A。
正确答案:C
考察知识点:动态规划
解析:观察到 11 行的输出为 dis[y][x],而我们枚举的范围是<y 与<x,所以第10 行计算的肯定是 dis[i+1][j+1],排除 AB,接着看 C 选项第一个转移,dis[i][j+1],说明这里是从第一维转移过来,而第 5 行也是从第一维转移过来的,使用的是v数组,选项 C 正确,排除 D 选项,本题选 C。
二、判断题(每题2分,共20分)
正确答案:错误
考察知识点:C++语法
解析:x*2-4=0;既不是判断语句,也不是赋值语句。它不是合法的C++语句,不能通过编译。
正确答案:正确
考察知识点:排列组合
解析:可能出现红红红,蓝红红,红蓝红,红红蓝,蓝蓝红,红蓝蓝,蓝红蓝。
正确答案:正确
考察知识点:数学知识
解析:杨辉三角,是二项式系数在三角形中的一种几何排列,在中国南宋数学家杨辉所著的《详解九章算法》中出现。
正确答案:错误
考察知识点:图
解析:有向完全图,(x,y)和(y,x)不算同一条边,所以有N*(N-1)条边。
正确答案:正确
考察知识点:哈希表
解析:极端情况,可以先将待查找元素放进哈希表中,再根据位置构造一个由 if-else 判断组成的哈希函数:当查找元素与某一项待查找相同时,返回对应的位置。虽然这样构造的哈希函数的时间复杂度较高,但满足不会产生冲突。
正确答案:正确
考察知识点:动态规划
解析:动态规划的时间复杂度⼀般为状态数*转移复杂度。
正确答案:错误
考察知识点:数学知识、基本数据类型
解析:梯形面积可能带有小数,不能直接/2。
正确答案:错误
考察知识点:图
解析:还可以使用深度优先搜索算法。
正确答案:错误
考察知识点:二叉排序树
解析:最好情况的时间复杂度为 O(1),即二叉排序树的根即为查找的元素。
正确答案:正确
考察知识点:二分查找
解析:函数
y
=
x
2
y= x^2
y=x2在值域[0,+∞]上具有单调性。
三、编程题(每题25分,共50分)
本题考察 排列组合
题目保证奖品的数量不多不少,每位同学都可以恰好分到⼀个奖品,且最后剩余的奖品不超过1个。两种情况:奖品不剩余;奖品剩余一个。
奖品不剩余,即n件奖品分给n个人。n个人中抽 a 0 a_0 a0个人拿奖品 a 0 a_0 a0,再剩下人数中抽 a 1 a_1 a1个人拿奖品 a 1 a_1 a1,依次类推。则总方案数为: C n a 0 ∗ C n − a 0 a 1 ∗ . . . C n − a 0 − a 1 − . . . − a m − 2 a m − 1 = C n a 0 ∗ C n − a 0 a 1 ∗ . . . C a m − 1 a m − 1 C_{n}^{a_0}*C_{n-a_0}^{a_1}*...C_{n-a_0-a_1-...-a_{m-2}}^{a_{m-1}} = C_{n}^{a_0}*C_{n-a_0}^{a_1}*...C_{a_{m-1}}^{a_{m-1}} Cna0∗Cn−a0a1∗...Cn−a0−a1−...−am−2am−1=Cna0∗Cn−a0a1∗...Cam−1am−1
奖品剩余1个,额外虚设一个人接受这个奖品,此时人数=奖品数,记为n,总方案数为: C n a 0 ∗ C n − a 0 a 1 ∗ . . . C n − a 0 − a 1 . . . − a m − 2 a m − 1 − 1 ∗ C 1 1 C_{n}^{a_0}*C_{n-a_0}^{a_1}*...C_{n-a_0-a_1...-a_{m-2}}^{a_{m-1}-1}*C_{1}^{1} Cna0∗Cn−a0a1∗...Cn−a0−a1...−am−2am−1−1∗C11,这和上边奖品不剩余的计算结果相同。
本题要计算很多组合,结合组合计算公式:c(n, m)=c(n-1, m-1)+c(n-1, m); ,用动态规划的思想进行预处理,否则会超时。
#include<bits/stdc++.h>
using namespace std;
const int N=1005, mod=1e9+7;
int c[N+5][N+5], arr[N+5];
//组合计算公式:c(n, m)=c(n-1, m-1)+c(n-1, m);
void init() {
c[0][0]=1;
for(int i=1; i<=N; i++) {
c[i][0]=c[i][i]=1;
for(int j=1; j<i; j++) {
c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod;
}
}
}
int main() {
init();
int t;
cin>>t;
while(t--) {
int n, m, sum=0;
cin>>n>>m;
for(int i=0; i<m; i++) {
cin>>arr[i];
sum += arr[i];
}
long long res=1;
for(int i=0; i<m; i++) {
res = res*c[sum][arr[i]]%mod;
sum -= arr[i];
}
cout<<res<<endl;
}
return 0;
}
本题考察 树、最近公共祖先(LCA)
同期6级题目相同,但数据规模增大了很多,要使用倍增法或树链剖分来解决。
倍增法求解LCA:一个很清晰的讲解视频:7分钟学会倍增法求解LCA
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n, fa[N]; //fa[i]节点i的直接领导
int dep[N]; //dep[i]节点i的深度
vector<int> G[N]; //存储节点关系
int anc[N][20]; //ans[i][j] 节点i往上跳2^j步的祖先节点
int maxnode[N]; //maxnode[i] 节点i往上一条链上最大的值
//计算所有节点的深度并同步记录节点i往前跳2^0步的祖先节点
void dfs(int u, int fa, int max_value) {
max_value=max(max_value, u);
maxnode[u]=max_value;
for(auto v : G[u]) {
if(v == fa) continue; //保证向下访问
dep[v]=dep[u]+1;
anc[v][0]=u; //v的直接祖先节点为u
dfs(v, u, max_value);
}
}
//初始化:计算所有节点的深度以及倍增法求LCA预处理
void init() {
dep[0]=1;
dfs(0, -1, 0);
for(int k=1; k<=18; k++) {
for(int i=1; i<=n; i++) {
anc[i][k]=anc[anc[i][k-1]][k-1]; //LCA预处理
}
}
}
//倍增法求最近公共祖先
int lca(int u, int v) {
//调平深度
if(dep[v] > dep[u]) swap(u, v);
for(int i=18; i>=0; i--) {
if(dep[anc[u][i]] >= dep[v]) {
u=anc[u][i];
}
}
if(u==v) return u; //特判
for(int i=18; i>=0; i--) {
if(anc[u][i] != anc[v][i]) {
u=anc[u][i];
v=anc[v][i];
}
}
return anc[u][0];
}
int main() {
scanf("%d", &n);
//输入编号为 i 的员工的直接领导fa[i]
for(int i=1; i<n; i++) {
scanf("%d", &fa[i]);
G[fa[i]].push_back(i);
G[i].push_back(fa[i]);
}
init(); //预处理
int q; //q场合作
scanf("%d", &q);
while(q--) {
int m, x, y;
scanf("%d%d", &m, &x);
for(int i=1; i<m; i++) {
scanf("%d", &y);
x = lca(x, y); //求x和y的最近公共祖先
}
printf("%d\n",maxnode[x]); //输出公共祖先中编号最大的员工
}
return 0;
}