2904 字
15 分钟
GESP 2025年C++三级编程题解析
2025/03/22
2025/09/30

202509-1 数组清零#

题目描述

小 A 有一个由 nn 个非负整数构成的数组 a=[a1,a2,,an]a = [a_1, a_2, \ldots, a_n]。他会对阵组 aa 重复进行以下操作,直到数组 aa 只包含 0。在一次操作中,小 A 会依次完成以下三个步骤:

  1. 在数组 aa 中找到最大的整数,记其下标为 kk。如果有多个最大值,那么选择其中下标最大的。
  2. 从数组 aa 所有不为零的整数中找到最小的整数 aja_j
  3. 将第一步找出的 aka_k 减去 aja_j

例如,数组 a=[2,3,4]a = [2, 3, 4] 需要 7 次操作变成 [0,0,0][0, 0, 0]

[2,3,4][2,3,2][2,1,2][2,1,1][1,1,1][1,1,0][1,0,0][0,0,0][2, 3, 4] \rightarrow [2, 3, 2] \rightarrow [2, 1, 2] \rightarrow [2, 1, 1] \rightarrow [1, 1, 1] \rightarrow [1, 1, 0] \rightarrow [1, 0, 0] \rightarrow [0, 0, 0]

小 A 想知道,对于给定的数组 aa,需要多少次操作才能使得 aa 中的整数全部变成 0。可以证明,aa 中整数必然可以在有限次操作后全部变成 0。你能帮他计算出答案吗?

输入格式

第一行,一个正整数 nn,表示数组 aa 的长度。

第二行,nn 个非负整数 a1,a2,,ana_1, a_2, \ldots, a_n,表示数组 aa 中的整数。

输出格式

一行,一个正整数,表示 aa 中整数全部变成 0 所需要的操作次数。

样例输入 1

3
2 3 4

样例输出 1

7

样例输入 2

5
1 3 2 2 5

样例输出 2

13

提示

对于所有测试点,保证 1n1001 \leq n \leq 1000ai1000 \leq a_i \leq 100

代码解析

这道题题目不难,主要考察的是打擂台求最大值和最小值,判断数组全部为0可以换种思路,如果数组的最大值是0,那数组元素肯定全部都是0。

#include<bits/stdc++.h>
using namespace std;
int main() {
int a[105], n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
int cnt = 0;
while (1) {
int k = 1, aj = 999;
for (int i = 1; i <= n; i++) {
if (a[i] != 0) aj = min(aj, a[i]);
// 这里使用 >= 保证有多个最大值选下标最大的
if (a[i] >= a[k]) k = i;
}
// 如果最大值是 0 说明已经全都是 0 了
if (a[k] == 0) break;
a[k] -= aj;
cnt++;
}
cout << cnt;
return 0;
}

202509-2 日历制作#

题目描述

小 A 想制作 20252025 年每个月的日历。他希望你能编写一个程序,按照格式输出给定月份的日历。

具体来说,第一行需要输出 MON TUE WED THU FRI SAT SUN,分别表示星期一到星期日。接下来若干行中依次输出这个月所包含的日期,日期的个位需要和对应星期几的缩写最后一个字母对齐。例如,202520259911 日是星期一,在输出九月的日历时,11 号的个位 11 就需要与星期一 MON 的最后一个字母 N 对齐。九月的日历输出效果如下:

MON TUE WED THU FRI SAT SUN
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

你能帮助小 A 完成日历的制作吗?

输入格式

一行,一个正整数 mm,表示需要按照格式输出 20252025mm 月的日历。

输出格式

输出包含若干行,表示 20252025mm 月的日历。

样例输入 1

9

样例输出 1

MON TUE WED THU FRI SAT SUN
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

样例输入 2

6

样例输出 2

MON TUE WED THU FRI SAT SUN
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

提示

对于所有测试点,保证 1m121 \leq m \leq 12

代码解析

这道题目给出了 9 月 1 日是星期一,所以其他月的 1 号是星期几需要从 9 月开始往前或者往后去推,推出来这个月的 1 号是星期几之后剩下的就简单了。

#include<bits/stdc++.h>
using namespace std;
int main() {
int m;
int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
cin >> m;
int sum = 0;
if (m >= 9) {
// 往后推 1 号是周几
for (int i = 9; i < m; i++) sum += days[i];
sum = sum % 7;
} else {
// 往前推 1 号是周几
for (int i = 8; i >= m; i--) sum += days[i];
sum = 7 - sum % 7;
}
cout << "MON TUE WED THU FRI SAT SUN" << endl;
// 输出前面的空格
for (int i = 1; i <= sum; i++)
cout << " ";
for (int i = 1; i <= days[m]; i++) {
if (i < 10) cout << " ";
else cout << ' ';
cout << i << ' ';
// 换行需要考虑到前面空格的数量
if ((i+sum) % 7 == 0) cout << endl;
}
return 0;
}

202506-1 奇偶校验#

题目描述

数据在传输过程中可能出错,因此接收方收到数据后通常会校验传输的数据是否正确,奇偶校验是经典的校验方式之一。

给定 nn 个非负整数 c1,c2,,cnc_1, c_2, \ldots, c_n 代表所传输的数据,它们的校验码取决于这些整数在二进制下 1 的数量 之和的奇偶性。如果这些整数在二进制下共有奇数个 1,那么校验码为 1;否则校验码为 0。你能求出这些整数的校验码吗?

输入格式

第一行,一个正整数 nn,表示所传输的数据量。

第二行,nn 个非负整数 c1,c2,,cnc_1, c_2, \ldots, c_n,表示所传输的数据。

输出格式

输出一行,两个整数,以一个空格分隔:

第一个整数表示 c1,c2,,cnc_1, c_2, \ldots, c_n 在二进制下 1 的总数量;

第二个整数表示校验码(0 或 1)。

样例输入 1

4
71 69 83 80

样例输出 1

13 1

样例输入 2

6
1 2 4 8 16 32

样例输出 2

6 0

提示

对于所有测试点,保证 1n1001 \leq n \leq 1000ci2550 \leq c_i \leq 255

代码解析

题目意思很好理解,需要统计 n 个数字中,每个数字的二进制中 1 的个数的和,并且判断 1 的个数是奇数还是偶数,使用二进制的数位剥离方法即可。

#include<bits/stdc++.h>
using namespace std;
int main() {
int n, cnt = 0;
cin >> n;
while (n--) {
int c;
cin >> c;
while (c) {
cnt += c % 2;
c /= 2;
}
}
cout << cnt << ' ' << cnt % 2;
return 0;
}

202506-2 分糖果#

题目描述

nn 位小朋友排成一队等待老师分糖果。第 ii 位小朋友想要至少 aia_i 颗糖果,并且分给他的糖果数量必须比分给 前一位小朋友的糖果数量更多,不然他就会不开心。

老师想知道至少需要准备多少颗糖果才能让所有小朋友都开心。你能帮帮老师吗?

输入格式

第一行,一个正整数 nn,表示小朋友的人数。

第二行,nn 个正整数 a1,a2,,ana_1, a_2, \ldots, a_n,依次表示每位小朋友至少需要的糖果数量。

输出格式

输出一行,一个整数,表示最少需要准备的糖果数量。

样例输入 1

4
1 4 3 3

样例输出 1

16

样例输入 2

15
314 15926 53589793 238462643 383279502 8 8 4 1 9 7 1 6 9 3

样例输出 2

4508143253

提示

对于所有测试点,保证 1n10001 \leq n \leq 10001ai1091 \leq a_i \leq 10^9

代码解析

我们需要分别计算每位同学需要多少糖果,如果 a[i] 大于前一位同学的糖果数量,则准备 a[i] 颗,否则需要比前一位同学 a[i-1] 多一颗

#include<bits/stdc++.h>
using namespace std;
int main() {
long long n, cnt = 0, a[1005];
cin >> n;
a[0] = 0; // 方便第一位同学a[1]比较
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] <= a[i-1])
a[i] = a[i-1] + 1;
cnt += a[i];
}
cout << cnt;
return 0;
}

202503-1 2025#

题目描述

小 A 有一个整数 xx,他想找到最小的正整数 yy 使得下式成立:

(x and y)+(x or y)=2025(x \ \operatorname{and} \ y) + (x \ \operatorname{or} \ y) = 2025

其中 and\operatorname{and} 表示二进制按位与运算,or\operatorname{or} 表示二进制按位或运算。如果不存在满足条件的 yy,则输出 1-1

输入格式

一行,一个整数 xx

输出格式

一行,一个整数,若满足条件的 yy 存在则输出 yy,否则输出 1-1

样例输入 1

1025

样例输出 1

1000

提示

对于所有测试点,保证 0x<20250 \leq x < 2025

(x and y)+(x or y)=2025(x \ \operatorname{and} \ y) + (x \ \operatorname{or} \ y) = 2025

其中:

  • and\operatorname{and} 表示按位与运算,运算符为 &\&
  • or\operatorname{or} 表示按位或运算,运算符为 |

代码解析

枚举法,从 1 开始枚举,找到符合条件的 i 直接结束主函数,若循环结束也没有找到则输出 -1

#include<bits/stdc++.h>
using namespace std;
int main() {
int x;
cin >> x;
for (int i = 1; i <= 2025; i++)
if ((x & i) + (x | i) == 2025) {
cout << i;
return 0;
}
cout << -1;
return 0;
}

202503-2 词频统计#

题目描述

在文本处理中,统计单词出现的频率是一个常见的任务。现在,给定 nn 个单词,你需要找出其中出现次数最多的单词。在本题中,忽略单词中字母的大小写(即 AppleappleAPPLEaPPle 等均视为同一个单词)。

请你编写一个程序,输入 nn 个单词,输出其中出现次数最多的单词。

输入格式

第一行,一个整数 nn,表示单词的个数;

接下来 nn 行,每行包含一个单词,单词由大小写英文字母组成。

输入保证,出现次数最多的单词只会有一个。

输出格式

输出一行,包含出现次数最多的单词(输出单词为小写形式)。

样例输入 1

6
Apple
banana
apple
Orange
banana
apple

样例输出 1

apple

提示

对于所有测试点,1n1001\leq n\leq 100,每个单词的长度不超过 3030,且仅由大小写字母组成。

代码解析

GESP 三级考点只有字符串以及数组,解这道题稍微麻烦一些。

解法 1:

words 数组中保存新的单词,在数组 a 中保存对应的单词出现的次数。

#include<bits/stdc++.h>
using namespace std;
int main() {
string words[105];
int a[105] = {0}, n, cnt = 0;
cin >> n;
while (n--) {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++)
if ('A' <= s[i] && s[i] <= 'Z')
s[i] += 'a'-'A';
int i = 0;
for (i = 0; i <= cnt; i++) {
// 若在数组中找到了单词,则对应的次数 a[i] 加一
if (words[i] == s) {
a[i]++;
break;
}
}
// 若上面的 for 循环正常结束,则说明单词第一次出现,做好记录,cnt是单词数
if (i > cnt) {
words[cnt] = s;
a[cnt]++;
cnt++;
}
}
int max = 0;
for (int i = 1; i < cnt; i++)
if (a[i] > a[max]) max = i;
cout << words[max];
return 0;
}

解法 2:

直接使用 words 字符串记录出现过的单词,每次使用 find()words 中找是否记录过单词,words 中单词 s 首字母的位置可以直接作为桶标记的下标。

例:

words:"apple banana orange"

a: {2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 0 ..... } 表示 2 个 apple,1 个 banana,3 个 orange

#include<bits/stdc++.h>
using namespace std;
int main() {
string words;
int n, a[3005] = {0};
cin >> n;
while (n--) {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++)
if ('A' <= s[i] && s[i] <= 'Z')
s[i] += 'a'-'A';
if (words.find(s) == -1)
// 若单词不在 words 中,则拼接,加空格方便最后的输出
words += s + ' ';
else
a[words.find(s)]++;
}
int max = 0;
for (int i = 1; i < 3005; i++)
if (a[i] > a[max]) max = i;
// 输出的时候以空格来区分
for (int i = max; words[i]!=' '; i++)
cout << words[i];
return 0;
}
GESP 2025年C++三级编程题解析
https://yezi.press/posts/gesp/gesp-cpp3-2025/
作者
Yezi 叶子
发布于
2025/03/22
许可协议
CC BY-NC-SA 4.0