GESP5级2024年03月真题解析
一、选择题(每题2分,共30分)
第 1 题 唯一分解定理描述的内容是( ) ?
A. 任意整数都可以分解为素数的乘积
B. 每个合数都可以唯一分解为一系列素数的乘积
C. 两个不同的整数可以分解为相同的素数乘积
D. 以上都不对
答案:B
解析:A错误,任何大于1的整数都可以分解为素数的乘积,而1没法分解。
B正确,唯一分解定理确实描述了该内容。
C错误,唯一分解定理,重点是唯一,不同的整数没法分解成相同的素数乘积。
D错误,ABC中B是正确的,并非都不对。
第 2 题 贪心算法的核心思想是( ) ?
A. 在每一步选择中都做当前状态下的最优选择
B. 在每一步选择中都选择局部最优解
C. 在每一步选择中都选择全局最优解
D. 以上都对
答案:A
解析:每一步选择应该做一个决策,得到一个解,不能搞混,BC直接排除,D说以上都对,已经两个不对,D错误,A是最终结果。
第 3 题 下面的 C++ 代码片段用于计算阶乘。 请在横线处填入( ) , 实现正确的阶乘计算。
int factorial(int n) {if (n == 0 || n == 1) {return 1;} else {_________________________________ // 在此处填入代码}
}
A. return n * factorial(n - 1);
B. return factorial(n - 1) / n;
C. return n * factorial(n);
D. return factorial(n / 2) * factorial(n / 2);
答案:A
解析:首先排除B,阶乘中没有除法,其次排除D,这是阶乘不是快速幂,排除C,因为会si递归,于是选A。
第 4 题 下面的代码片段用于在双向链表中删除一个节点。 请在横线处填入( ) , 使其能正确实现相应功能
void deleteNode(DoublyListNode*& head, int value) {DoublyListNode* current = head;while (current != nullptr && current->val != value) {current = current->next;}if (current != nullptr) {if (current->prev != nullptr) {____________________________________ // 在此处填入代码} else {head = current->next;}if (current->next != nullptr) {current->next->prev = current->prev;}delete current;}
}
A. if (current->next != nullptr) current->next->prev = current->prev;
B. current->prev->next = current->next;
C. delete current->next;
D. current->prev = current->next;
答案:B
解析:如果current不是第一个节点,那么他的前驱就要指向current的后一个节点,这一步不需要判断后面是否为空,因为如果是最后一个元素,前驱的后继节点就直接指向nullptr了,如此巧妙,直接填写B
第 5 题 辗转相除法也被称为( )
A. 高斯消元法
B. 费马定理
C. 欧几里德算法
D. 牛顿迭代法
答案:C
解析:辗转相除法跟消元没有关系,他也不是定理,牛顿迭代法是平方根算法,所以选C,欧几里得算法。
第 6 题 下面的代码片段用于计算斐波那契数列。 该代码的时间复杂度是( ) ?
int fibonacci(int n) {if (n <= 1) {return n;} else {return fibonacci(n - 1) + fibonacci(n - 2);}
}
A. O(1)
B. O(n)
C. O(2^n)
D. O(logn)
答案:C
解析:每次递归都调用自身两次,看着就像O(2^n),于是选C。
第 7 题 下面的代码片段用于将两个高精度整数进行相加。 请在横线处填入( ) , 使其能正确实现相应功能。
string add(string num1, string num2) {string result;int carry = 0;int i = num1.size() - 1, j = num2.size() - 1;while (i >= 0 || j >= 0 || carry) {int x = (i >= 0) ? num1[i--] - '0' : 0;int y = (j >= 0) ? num2[j--] - '0' : 0;int sum = x + y + carry;carry = sum / 10;_______________________________________}return result;
}
A. result = to_string(sum % 10) + result;
B. result = to_string(carry % 10) + result;
C. result = to_string(sum / 10) + result;
D. result = to_string(sum % 10 + carry) + result;
答案:A
解析:高精度加法中,carry是进位,sum是当前位两个数的和+进位carry,我们的结果肯定要加sum,但是我们都是遇到进位就写个位进十位,所以真正存到结果的是个位,即sum%10,选A。
第 8 题 给定序列: 1, 3, 6, 9, 17, 31, 39, 52, 61, 79, 81, 90, 96。 使用以下代码进行二分查找查找元素 82时, 需要循环多少次, 即最后输出的 times 值为( ) 。
int binarySearch(const std::vector<int>& arr, int target) {int left = 0;int right = arr.size() - 1;int times = 0;while (left <= right) {times ++;int mid = left + (right - left) / 2;if (arr[mid] == target) {cout << times << endl;return mid;} else if (arr[mid] < target) {left = mid + 1;} else {right = mid - 1;}}cout << times << endl;return -1;
}
A. 2
B. 5
C. 3
D. 4
答案:D
解析:
ARR={1,3,6,9,17,31,39,52,61,79,81,90,96},TARGET=82
0 1 2 3 4 5 6 7 8 9 10 11 12
模拟二分过程如下
times=1,left=0,right=12,mid=0+(12-0)/2=6,a[6]=39,39<82,left=mid+1=7
times=2,left=7,right=12,mid=7+(12-7)/2=9,a[9]=79,79<82,left=mid+1=10
times=3,left=10,right=12,mid=10+(12-10)/2=11,a[11]=90,90>82,right=mid-1=10
times=4,left=10,right=10,mid=10+(10-10)/2=10,a[10]=81,81<82,left=mid+1=11
left=11,right=10,跳出循环,times=4,结果为4,选D
第 9 题 下面的代码片段用于判断一个正整数是否为素数。 请对以下代码进行修改, 使其能正确实现相应功能。 ( )
bool isPrime(int num) {if (num < 2) {return false;}for (int i = 2; i * i < num; ++i) {if (num % i == 0) {return false;}}return true;
}
A. num < 2 应该改为 num <= 2
B. 循环条件 i * i < num 应该改为 i * i <= num
C. 循环条件应该是 i <= num
D. 循环体中应该是 if (num % i != 0)
答案:B
解析:A错误,num<=2,当判断的数为2,程序会告诉我们:2不是质数,判断错误。
B正确,i*i<num对大多数都友好,但对质数的平方不太友好,因为质数的平方只有1、质数、质数的平方三个因数,现在中间的质数,他的平方<原数,不能枚举到,于是就发生了误判,所以把i*i<num改成小于等于就行了。
C错误,i<=num虽然可以跑,但是你不能把人家效率高的代码改成效率低的啊!
D错误,num%i!=0时只要不被整除,就不行,妥妥的质数合数不分明啊!
第 10 题 在埃拉托斯特尼筛法中, 要筛选出不大于 n 的所有素数, 最外层循环应该遍历什么范围( ) ?
vector<int> sieveOfEratosthenes(int n) {std::vector<bool> isPrime(n + 1, true);std::vector<int> primes;_______________________ {if (isPrime[i]) {primes.push_back(i);for (int j = i * i; j <= n; j += i) {isPrime[j] = false;}}}for (int i = sqrt(n) + 1; i <= n; ++i) {if (isPrime[i]) {primes.push_back(i);}}return primes;
}
A. for (int i = 2; i <= n; ++i)
B. for (int i = 1; i < n; ++i)
C. for (int i = 2; i <= sqrt(n); ++i)
D. for (int i = 1; i <= sqrt(n); ++i)
答案:C
解析:1不是质数,要从2开始枚举;要枚举到sqrt(n),这样能给sqrt(n)+1~n的未标记合数的质数留出空间,否则后面就白写了。选C
第 11 题 素数的线性筛法时间复杂度为( ) 。
A. O(n)
B. O(n log log n)
C. O(n log n)
D. O(n^2)
答案:A
解析:线性筛法的时间为O(n),毕竟是线性。
第 12 题 归并排序的基本思想是( ) 。
A. 动态规划
B. 分治
C. 贪心算法
D. 回溯算法
答案:B
解析:考纲中有一项叫分(而)治(之),归并排序就是将要排序的数组分成两部分,递归处理(治)两部分,最后合并成一个有序数组,而分治就是三步:分、治、并,所以选B。
另见:我的博客->归并排序Merge Sort
第 13 题 在快速排序中, 选择的主元素(pivot) 会影响算法的( ) 。
A. 不影响
B. 时间复杂度
C. 空间复杂度
D. 时间复杂度和空间复杂度
答案:D
注意!!!GESP官方给的答案是B,但实际答案是D!!!
解析:快速排序的pivot是用于划分数组,化的靠中间,递归层数少,每层都O(n),时间O(nlogn),递归栈空间O(logn),化的靠两边,递归层数多,每层都O(n),时间O(n^2),递归栈空间O(n),因此这题要选D,但是在练习的平台上都是B,所以记得就行。
另见:我的博客->快速排序Quick Sort
第 14 题 递归函数在调用自 身时, 必须满足( ) , 以避免无限递归?
A. 有终止条件
B. 函数参数递减(或递增)
C. 函数返回值固定
D. 以上都对
答案:A
解析:A正确,递归函数必须有终止条件,否则会无尽死循环,当然,写一大堆if的递归函数也是允许的,你得确保所有递归函数最后都能走向所有条件都不满足的直接退出情况的函数;
B错误,我可以递归遍历无序数组,而且下标就用全局变量咋地~
C错误,我还是遍历无序数组,这回我加入返回数组中的值,看你怎么办~
D错误,BC都错了,你不也错了。
第 15 题 假设给定链表为: 1->3->5->7->nullptr, 若调用 searchValue(head, 5) , 函数返回值为( ) 。
int searchValue(ListNode* head, int target) {while (head != nullptr) {if (head->val == target) {return 1;}head = head->next;}return 0;
}
A. 返回 1
B. 返回 0
C. 死循环, 无法返回
D. 返回 -1
答案:A
解析:1 3 5 7中确实包含5,而且函数写的没问题,再找到时返回1,因此返回1,选A
二、判断题(每题2分,共20分)
第 1 题 辗转相除法用于求两个整数的最大公约数。
答案:正确
解析:辗转相除法就是这样做的,本身求GCD(最大公因数)
第 2 题 插⼊排序的时间复杂度是O(nlogn)。
答案:错误
解析:插入排序的时间复杂度是O(n^2),只有折半插入排序才是O(nlogn)
第 3 题 ⼆分查找要求被搜索的序列是有序的,否则⽆法保证正确性。
答案:正确
解析:二分查找只适用于有序序列,无序的请走开,仅为没法无脑排除一半
第 4 题 使⽤贪⼼算法解决问题时,每⼀步的局部最优解⼀定会导致全局最优解。
答案:错误
解析:遇到决策相互独立的还好,遇到前面影响后面的不能用贪心。
第 5 题 分治算法的核⼼思想是将⼀个⼤问题分解成多个相同或相似的⼦问题进⾏解决,最后合并得到原问题的解。
答案:正确
解析:分治算法就是分解、处理、合并,这句话描述完全正确
第 6 题 分治算法的典型应⽤之⼀是归并排序,其时间复杂度为O(nlogn)。
答案:正确
解析:归并排序在任何情况下都是O(nlogn),另见我的博客->归并排序Merge Sort
第 7 题 素数表的埃⽒筛法和线性筛法的时间复杂度都是O(nloglogn)。
答案:错误
解析:线性筛法的时间为O(n)
第 8 题 贪⼼算法是⼀种可以应⽤于所有问题的通⽤解决⽅案。
答案:错误
解析:前面的决策影响后面,你不BOOM了,贪心不适用于前面影响后面的问题
第 9 题 单链表和双链表都可以在常数时间内实现在链表头部插⼊或删除节点的操作。
答案:正确
解析:在表头插入,只需要让该节点的下一个改成原head的下一个,head的下一个改成该节点即可,双向链表还要额外进行前去修改操作,但是不管怎样都是O(1)的。
第 10 题 在C语⾔中,递归的实现⽅式通常会占⽤更多的栈空间,可能导致栈溢出
答案:正确
解析:递归需要栈空间,在无限递归时会栈溢出引发代码崩溃。别再尝逝
int main()
{return main();
}
了。
三、编程题(每题25分,共50分)
1 B3968 成绩排序(普及-)
题目描述
有 n 名同学,每名同学有语文、数学、英语三科成绩,你需要按照如下规则对所有同学的成绩从高到低排序:
- 比较总分,高者靠前;
- 如果总分相同,则比较语文和数学两科的总分,高者靠前;
- 如果仍相同,则比较语文和数学两科的最高分,高者靠前;
- 如果仍相同,则二人并列。
你需要输出每位同学的排名,如遇 x 人并列,则他们排名相同,并留空后面的 x−1 个名次。例如,有 3 名同学并列第 1,则后一名同学自动成为第 4 名。
输入格式
第一行一个整数 N,表示同学的人数。
接下来 N 行,每行三个非负整数 ci,mi,ei 分别表示该名同学的语文、数学、英语成绩。
输出格式
输出 N 行,按输入同学的顺序,输出他们的排名。
注意:请不要按排名输出同学的序号,而是按同学的顺序输出他们各自的排名。
输入输出样例
输入 #1
6 140 140 150 140 149 140 148 141 140 141 148 140 145 145 139 0 0 0
输出 #1
1 3 4 4 2 6
说明/提示
- 对 30% 的数据,N≤100,且所有同学总分各不相同。
- 对全部的测试数据,保证 2≤N≤104,0≤ci,mi,ei≤150。
题目大意
给定n个同学的语文、数学、英语成绩,求每个同学都得了第几名。
题目分析
比较方式为:
1 比较总分
2 比较语数总分
3 比较语数更高分
4 并列
意思是说,如果被比较的两个值总分不相同,直接分出胜负;
相等时,如果语文数学的总分不相同,直接分出胜负;
相等时,如果语文数学的更高分不相同,分出胜负,否则并列。
并列时,他的排名就等于前一个,否则就是序号
举个栗子:3 4 3 7 5 4 7
排序后结果为3 3 4 4 5 7 7
现在编号:
第一个值3,没有前一个,就是第一名。
第二个值3,前一个与他相等,就是第一名。
第三个值4,前一个与他不等,就是第三名。
第四个值4,前一个与他相等,就是第三名。
第五个值5,前一个与他不等,就是第五名。
第六个值7,前一个与他不等,就是第六名。
第七个值7,前一个与他相等,就是第六名。
因此名次为1 1 3 3 5 6 6
明白了这点,我们就可以用自定义比较函数对成绩数组(node类型)进行排序了
用到函数std::sort(auto iter1,auto iter2,bool compare),iter1和iter2是迭代器,表示排序部分的起始位置和终止位置的下一个位置。compare是bool类型的自定义比较函数,也就是比较方式
解题步骤
1 读入数据并排序
2 枚举名次并获取学号
3 输出各个学号的排名
1 读入数据并排序
自定义比较函数:数组按照你的想法有序时,相邻元素之间的关系,关系达成true,反之false。
我们设了结构体node,表示每个同学的信息,有学号、语数英成绩、总分(方便调用)。
这样写
struct node
{int id,c,m,e,tot;
}sc[10001];
sc就是数组名了,id c m e tot分别表示学号、语数英成绩、总分。
读入数据的时候要这样做
int n;
cin>>n;
for(int i=1;i<=n;i++)
{cin>>sc[i].c>>sc[i].m>>sc[i].e;sc[i].id=i,sc[i].tot=sc[i].c+sc[i].m+sc[i].e;
}
然后是自定义比较函数
bool cmp(node x,node y)
{if(x.tot>y.tot)return 1;if(x.tot<y.tot)return 0;if(x.c+x.m>y.c+y.m)return 1;if(x.c+x.m<y.c+y.m)return 0;if(max(x.c,x.m)>max(y.c,y.m))return 1;return 0;
}
这里max(x)小于等于max(y)时就return 0了,因为小于是需要小前大后,等于是无所谓顺序。
好了,现在用一条语句就能完成排序了
sort(sc+1,sc+n+1,cmp);
2 枚举名次获取学号
为了方便,我们设一个数组int a[10001]表示最终结果,下标为学号,值为名次
第i名的信息是sc[i],因此a[sc[i].id]就是i,但是如果当前与上一名(sc[i-1])并列,就要设为a[sc[i-1].id],但是我怎么判断并列啊,cmp函数也不能正确判断,不如写个is_equal函数吧
bool is_equal(node x,node y)
{if(x.tot!=y.tot)return 0;if(x.c+x.m!=y.c+y.m)return 0;if(max(x.c,x.m)!=max(y.c,y.m))return 0;return 1;
}
相等返回1,不相等返回0
现在就有了这段代码
for(int i=1;i<=n;i++)
{a[sc[i].id]=i;if(i>1){if(is_equal(sc[i],sc[i-1]))a[sc[i].id]=a[sc[i-1].id];}
}
3 输出各个学号的排名
这个不用说了,看代码吧
for(int i=1;i<=n;i++)cout<<a[i]<<endl;
完整代码
#include<bits/stdc++.h>
using namespace std;
struct node
{int id,c,m,e,tot;
}sc[10001];
int a[10001];
bool cmp(node x,node y)
{if(x.tot>y.tot)return 1;if(x.tot<y.tot)return 0;if(x.c+x.m>y.c+y.m)return 1;if(x.c+x.m<y.c+y.m)return 0;if(max(x.c,x.m)>max(y.c,y.m))return 1;return 0;
}
bool is_equal(node x,node y)
{if(x.tot!=y.tot)return 0;if(x.c+x.m!=y.c+y.m)return 0;if(max(x.c,x.m)!=max(y.c,y.m))return 0;return 1;
}
int main()
{int n;cin>>n;for(int i=1;i<=n;i++){cin>>sc[i].c>>sc[i].m>>sc[i].e;sc[i].id=i,sc[i].tot=sc[i].c+sc[i].m+sc[i].e;}sort(sc+1,sc+n+1,cmp);for(int i=1;i<=n;i++){a[sc[i].id]=i;if(i>1){if(is_equal(sc[i],sc[i-1]))a[sc[i].id]=a[sc[i-1].id];}}for(int i=1;i<=n;i++)cout<<a[i]<<endl;return 0;
}
AC截图
小结
其实这题并不难,主要就是代码有点长,需要细致的分析和足够多的知识储备,还结合了其他级别的知识点(如四级的结构体),打好基础是关键。
2 B3969 B-Smooth数(普及-)
题目描述
小杨同学想寻找一种名为 B-smooth 数的正整数。
如果一个正整数的最大质因子不超过 B,则该正整数为 B-smooth 数。小杨同学想知道,对于给定的 n 和 B,有多少个不超过 n 的 B-smooth 数。
输入格式
第一行包含两个正整数 n 和 B,含义如题面所示。
输出格式
输出一个非负整数,表示不超过 n 的 B-smooth 数的数量。
输入输出样例
输入 #1
10 3
输出 #1
7
说明/提示
数据规模与约定
子任务 Subtask | 得分 Score | n≤ | B |
---|---|---|---|
1 | 30 | 10^3 | 1≤B≤10^3 |
2 | 30 | 10^6 | n≤B≤10^6 |
3 | 40 | 10^6 | 1≤B≤10^6 |
对全部的测试数据,保证 1≤n,B≤10^6。
题目大意
定义B-Smooth数:最大质因子不超过B的数。
给定n、B求1~n中B-Smooth数的数量。
题目分析
对于每个i(i<=n),可以因数分解,每次找到新的质因数就将最大质因数设置为他,最后判断是否<=b,
当然可以优化,当mx>b时,直接break,这样不合法直接被排除。
因数分解的题目我已经写过了,就在这里:因数分解(GESP5级202306编程T1)题解-CSDN博客
现在...可以直接看代码了吧
完整代码
#include<bits/stdc++.h>
using namespace std;
int main()
{int n,b,ans=0;cin>>n>>b;for(int i=1;i<=n;i++){int mx=1;int t=i;for(int j=2;j*j<=t;j++){if(t%j==0){mx=j;if(mx>b)break;while(t%j==0)t/=j;}}if(t>1)mx=t;if(mx<=b)ans++;}cout<<ans<<endl;return 0;
}
这个做法是可以AC的,不会TLE。
AC截图
小结
本题并不难,GESP5级大部分题目赌一把时间就能过。如果有想要更快的可以搜一下题解。
结语
祝各位考生在考场上超常发挥,斩获高分!
我的文章已经写了9230字,给个三连不过分吧。