3854 字
19 分钟
GESP 2023年C++四级编程题解析

202312-1 小杨的字典#

题目描述

在遥远的星球,有两个国家 A 国和 B 国,他们使用着不同的语言:A 语言和 B 语言。小杨是 B 国的翻译官,他的工作是将 A 语言的文章翻译成 B 语言的文章。

为了顺利完成工作,小杨制作了一本字典,里面记录了 NN 个 A 语言单词对应的 B 语言单词,巧合的是,这些单词都 由地球上的 26 个小写英文字母组成。

小杨希望你写一个程序,帮助他根据这本字典翻译一段 A 语言文章。这段文章由标点符号 !()-.[].{}\|;:'",./?<> 和一些 A 语言单词构成,每个单词之间必定由至少一个标点符号分割,你的程序需要把这段话中的所有 A 语言单词替换成它的 B 语言翻译。特别地,如果遇到不在字典中的单词,请使用大写 UNK 来替换它。

例如,小杨的字典中包含 22 个 A 语言单词 abcd,它们的 B 语言翻译分别为 adef,那么我们可以把 A 语言文章 abc.d.d.abc.abcd. 翻译成 B 语言文章 a.def.def.a.UNK. 其中,单词 abcd 不在词典内,因此我们需要使用 UNK 来替换它。

输入格式

第一行一个整数 NN,表示词典中的条目数。保证 N100N \le 100

接下来 NN 行,每行两个用单个空格隔开的字符串 AABB ,分别表示字典中的一个 A 语言单词以及它对应的 B 语言翻译。保证所有 AA 不重复;保证 AABB 的长度不超过 1010

最后一行一个字符串 SS ,表示需要翻译的 A 语言文章。保证字符串 SS 的长度不超过 10001000,保证字符串 SS 只包含小写字母以及标点符号 !()-.[].{}\|;:'",./?<>

输出格式

输出一行,表示翻译后的结果。

样例输入 1

2
abc a
d def
abc.d.d.abc.abcd

样例输出 1

a.def.def.a.UNK

样例输入 2

3
abc a
d def
abcd xxxx
abc,(d)d!-abc?abcd

样例输出 2

a,(def)def!-a?xxxx

样例输入 3

1
abcdefghij klmnopqrst
!()-[]{}\|;:'",./?<>abcdefghijklmnopqrstuvwxyz

样例输出 3

!()-[]{}\|;:'",./?<>UNK

代码解析

参考代码

#include<bits/stdc++.h>
using namespace std;
struct dict {
string a, b;
} d[100];
int N;
string str, word;
bool is_flag(char c) {
string s = "!()-.[].{}\\|;:\'\",./?<>";
for (int i = 0; i < s.size(); i++) {
if (c == s[i])
return true;
}
return false;
}
string find(string s) {
if (s == "") return s;
for (int i = 0; i < N; i++) {
if (s == d[i].a)
return d[i].b;
}
return "UNK";
}
int main() {
cin >> N;
for (int i = 0; i < N; i++) {
cin >> d[i].a >> d[i].b;
}
cin >> str;
str += '.';
for (int i = 0; i < str.size(); i++) {
if (is_flag(str[i])) {
cout << find(word);
if (i < str.size() - 1)
cout << str[i];
word = "";
} else {
word += str[i];
}
}
return 0;
}

202312-2 田忌赛马#

题目描述

你要和田忌赛马。你们各自有 NN 匹马,并且要进行 NN 轮比赛,每轮比赛,你们都要各派出一匹马决出胜负。

你的马匹的速度分别为 u1,u2,,unu_1,u_2,\cdots,u_n,田忌的马匹的速度分别为 v1,v2,,vnv_1,v_2,\cdots,v_n。田忌会按顺序派出他的马匹,请问你要如何排兵布阵,才能赢得最多轮次的比赛?巧合的是,你和田忌的所有马匹的速度两两不同,因此不可能出现平局。

输入格式

第一行一个整数 NN。保证 1N5×1041\le N \le 5\times 10^4

接下来一行 NN 个用空格隔开的整数,依次为 u1,u2,,unu_1,u_2,\cdots,u_n,表示你的马匹们的速度。保证 1ui2N1\le u_i\le 2N

接下来一行 NN 个用空格隔开的整数,依次为 v1,v2,,vnv_1,v_2,\cdots,v_n,表示田忌的马匹们的速度。保证 1vi2N1\le v_i\le 2N

输出格式

输出一行,表示你最多能获胜几轮。

样例输入 1

3
1 3 5
2 4 6

样例输出 1

2

样例输入 2

5
10 3 5 8 7
4 6 1 2 9

样例输出 2

5

提示

样例解释 1

第 1 轮,田忌派出速度为 2 的马匹,你可以派出速度为 3 的马匹迎战,本轮你获胜。

第 2 轮,田忌派出速度为 4 的马匹,你可以派出速度为 5 的马匹迎战,本轮你获胜。

第 3 轮,田忌派出速度为 6 的马匹,你可以派出速度为 1 的马匹迎战,本轮田忌获胜。

如此,你可以赢得 2 轮比赛。

代码解析

参考代码

#include<bits/stdc++.h>
using namespace std;
int main() {
int a[50005], b[50005], n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++)
cin >> b[i];
sort(a, a+n);
sort(b, b+n);
int i = 0, j = 0, cnt = 0;
while (i < n && j < n) {
// 如果我们的第 i 匹马慢于田忌第 j 匹,则拿我们下一匹马继续试
if (a[i] <= b[j]) {
i++;
} else {
i++, j++;
}
}
cout << j;
return 0;
}

202309-1 进制转换#

题目描述

NN 进制数指的是逢 NN 进一的计数制。例如,人们日常生活中大多使用十进制计数,而计算机底层则一般使用二进制。除此之外,八进制和十六进制在一些场合也是常用的计数制(十六进制中,一般使用字母 A 至 F 表示十至十五;本题中,十一进制到十五进制也是类似的)。

在本题中,我们将给出 个不同进制的数。你需要分别把它们转换成十进制数。

输入格式

输入的第一行为一个十进制表示的整数 NN。接下来 NN 行,每行一个整数 KK,随后是一个空格,紧接着是一个 KK 进制数,表示需要转换的数。保证所有 KK 进制数均由数字和大写字母组成,且不以 00 开头。保证 KK 进制数合法。

保证 N1000N \le 1000;保证 2K162 \le K \le 16

保证所有 KK 进制数的位数不超过 99

输出格式

输出 NN 行,每一个十进制数,表示对应 KK 进制数的十进制数值。

样例输入 1

2
8 1362
16 3F0

样例输出 1

754
1008

样例输入 2

2
2 11011
10 123456789

样例输出 2

27
123456789

提示

对于任意一个 LLKK 进制数,假设其最右边的数位为第 00 位,最左边的数位为第 L1L-1 位,我们只需要将其第 ii 位的数码乘以权值 KiK^i,再将每位的结果相加,即可得到原 KK 进制数对应的十进制数。下面是两个例子:

  1. 八进制数 1362 对应的十进制数为:1×83+3×82+6×81+2×80=7541×8^3+3×8^2+6×8^1+2×8^0=754

  2. 十六进制数 3F0 对应的十进制数为:3×162+15×161+0×160=10083×16^2+15×16^1+0×16^0=1008

代码解析

参考代码

#include<bits/stdc++.h>
using namespace std;
int char_to_int(char c) {
if ('0' <= c && c <= '9')
return c - '0';
return c - 'A' + 10;
}
long long k_to_dec(int k, string s) {
long long num = 0;
for (int i = 0; i < s.size(); i++) {
num *= k;
num += char_to_int(s[i]);
}
return num;
}
int main() {
int n;
cin >> n;
while (n--) {
int k;
string s;
cin >> k >> s;
cout << k_to_dec(k, s) << endl;
}
return 0;
}

202309-2 变长编码#

题目描述

小明刚刚学习了三种整数编码方式:原码、反码、补码,并了解到计算机存储整数通常使用补码。但他总是觉得,生活中很少用到 23112^{31}-1 这么大的数,生活中常用的 01000\sim 100 这种数也同样需要用 44 个字节的补码表示,太浪费了些。 热爱学习的小明通过搜索,发现了一种正整数的变长编码方式。这种编码方式的规则如下:

  1. 对于给定的正整数,首先将其表达为二进制形式。例如,(0){10}=(0){2}(0)_{\{10\}}=(0)_{\{2\}}(926){10}=(1110011110){2}(926)_{\{10\}}=(1110011110)_{\{2\}}

  2. 将二进制数从低位到高位切分成每组 77 bit,不足 77bit 的在高位用 00 填补。例如,(0){2}(0)_{\{2\}} 变为00000000000000 的一组,(1110011110){2}(1110011110)_{\{2\}} 变为 0011110001111000001110000111 的两组。

  3. 由代表低位的组开始,为其加入最高位。如果这组是最后一组,则在最高位填上 00,否则在最高位填上 11。于是,00 的变长编码为 0000000000000000 一个字节, 926926 的变长编码为 10011110100111100000011100000111 两个字节。

这种编码方式可以用更少的字节表达比较小的数,也可以用很多的字节表达非常大的数。例如,987654321012345678987654321012345678 的二进制为 (0001101 1011010 0110110 1001011 1110100 0100110 1001000 0010110 1001110){2}(0001101 \ 1011010 \ 0110110 \ 1001011 \ 1110100 \ 0100110 \ 1001000 \ 0010110 \ 1001110)_{\{2\}},于是它的变长编码为(十六进制表示) CE 96 C8 A6 F4 CB B6 DA 0D,共 99 个字节。

你能通过编写程序,找到一个正整数的变长编码吗?

输入格式

输入第一行,包含一个正整数 NN。约定 0N10180\le N \le 10^{18}

输出格式

输出一行,输出 NN 对应的变长编码的每个字节,每个字节均以 22 位十六进制表示(其中, A-F 使用大写字母表示),两个字节间以空格分隔。

样例输入 1

0

样例输出 1

00

样例输入 2

926

样例输出 2

9E 07

样例输入 3

987654321012345678

样例输出 3

CE 96 C8 A6 F4 CB B6 DA 0D

代码解析

参考代码

#include<bits/stdc++.h>
using namespace std;
char int_to_char(int a);
string dec_to_longbin(string s1);
string longbin_to_hex(string s);
int main() {
long long a;
string s, s1, s2;
cin >> a;
do {
s1 += int_to_char(a & 1);
a = a >> 1;
} while (a);
s2 = dec_to_longbin(s1) + ' ';
for (int i = 0; i < s2.size(); i++) {
if (s2[i] != ' ') {
s += s2[i];
} else {
cout << longbin_to_hex(s) << ' ';
s = "";
}
}
return 0;
}
char int_to_char(int a) {
if (a < 10)
return a + '0';
return a - 10 + 'A';
}
string dec_to_longbin(string s1) {
string s, s2;
int cnt = 0;
for (int i = 1; i < s1.size(); i++) {
cnt++;
s = s1[i-1] + s;
if (i % 7 == 0) {
s = '1' + s + ' ';
s2 += s;
s = "";
cnt = 0;
}
}
s = s1[s1.size() - 1] + s;
cnt++;
for (int j = 0; j < 8-cnt; j++)
s = '0' + s;
return s2 += s;
}
string longbin_to_hex(string s) {
string s1;
int n = 0, m = 0;
for (int i = 0; i < 4; i++) {
n *= 2;
n += s[i] - '0';
}
s1 += int_to_char(n);
for (int i = 4; i < 8; i++) {
m *= 2;
m += s[i] - '0';
}
s1 += int_to_char(m);
return s1;
}

202306-1 幸运数#

题目描述

小明发明了一种 “幸运数”。一个正整数,其偶数位不变(个位为第 11 位,十位为第 22 位,以此类推),奇数位做如下变换:将数字乘以 77,如果不大于 99 则作为变换结果,否则把结果的各位数相加,如果结果不大于 99 则作为变换结果,否则(结果仍大于 99)继续把各位数相加,直到结果不大于 99,作为变换结果。变换结束后,把变换结果的各位数相加,如果得到的和是 88 的倍数,则称一开始的正整数为幸运数。

例如,1634716347:第 11 位为 77,乘以 77 结果为 4949,大于 99,各位数相加为 1313,仍大于 99,继续各位数相加,最后结果为 44;第 33 位为 33,变换结果为 33;第 55 位为 11,变换结果为 77。最后变化结果为 7634476344,对于结果 7634476344 其各位数之和为 2424,是 88 的倍数。因此 1634716347 是幸运数。

输入格式

输入第一行为正整数 NN,表示有 NN 个待判断的正整数。约定 1N201 \le N \le 20

从第 22 行开始的 NN 行,每行一个正整数,为待判断的正整数。约定这些正整数小于 101210^{12}

输出格式

输出 NN 行,对应 NN 个正整数是否为幸运数,如是则输出 T,否则输出 F

提示:不需要等到所有输入结束再依次输出,可以输入一个数就判断一个数并输出,再输入下一个数。

样例输入 1

2
16347
76344

样例输出 1

T
F

代码解析

参考代码

#include<bits/stdc++.h>
using namespace std;
int f(int a) {
int sum = 0;
while (a) {
sum += a % 10;
a /= 10;
}
if (sum > 9)
return f(sum);
return sum;
}
int main() {
int n;
cin >> n;
while (n--) {
long long num, cnt = 0;
cin >> num;
bool flag = true;
while (num) {
int ans = num % 10;
if (flag) {
ans *= 7;
ans = f(ans);
}
cnt += ans;
num /= 10;
flag = !flag;
}
cout << (cnt % 8 == 0 ? 'T' : 'F') << endl;
}
return 0;
}

202306-2 图像压缩#

题目描述

图像是由很多的像素点组成的。如果用 00 表示黑,255255 表示白,00255255 之间的值代表不同程度的灰色,则可以用一个字节表达一个像素(取值范围为十进制 0-255、十六进制 00-FF)。这样的像素组成的图像,称为 256256 级灰阶的灰度图像。

现在希望将 256256 级灰阶的灰度图像压缩为 1616 级灰阶,即每个像素的取值范围为十进制 0-15、十六进制 0-F。压缩规则为:统计出每种灰阶的数量,取数量最多的前 1616 种灰阶(如某种灰阶的数量与另外一种灰阶的数量相同,则以灰阶值从小到大为序),分别编号 0-F(最多的编号为 0,以此类推)。其他灰阶转换到最近的 1616 种灰阶之一,将某个点的灰阶值(灰度,而非次数)与 1616 种灰阶中的一种相减,绝对值最小即为最近,如果绝对值相等,则编号较小的灰阶更近。

输入格式

输入第 11 行为一个正整数 n(10n20)n(10\le n \le 20),表示接下来有 nn 行数据组成一副 256256 级灰阶的灰度图像。

22 行开始的 nn 行,每行为长度相等且为偶数的字符串,每两个字符用十六进制表示一个像素。约定输入的灰度图像至少有 1616 种灰阶。约定每行最多 2020 个像素。

输出格式

第一行输出压缩选定的 1616 种灰阶的十六进制编码,共计 3232 个字符。

第二行开始的 nn 行,输出压缩后的图像,每个像素一位十六进制数表示压缩后的灰阶值。

样例输入 1

10
00FFCFAB00FFAC09071B5CCFAB76
00AFCBAB11FFAB09981D34CFAF56
01BFCEAB00FFAC0907F25FCFBA65
10FBCBAB11FFAB09981DF4CFCA67
00FFCBFB00FFAC0907A25CCFFC76
00FFCBAB1CFFCB09FC1AC4CFCF67
01FCCBAB00FFAC0F071A54CFBA65
10EFCBAB11FFAB09981B34CFCF67
01FFCBAB00FFAC0F071054CFAC76
1000CBAB11FFAB0A981B84CFCF66

样例输出 1

ABCFFF00CB09AC07101198011B6776FC
321032657CD10E
36409205ACC16D
B41032657FD16D
8F409205ACF14D
324F326570D1FE
3240C245FC411D
BF4032687CD16D
8F409205ACC11D
B240326878D16E
83409205ACE11D

提示

【样例 11 解释】

灰阶 ABCFFF 出现 1414 次,00 出现 1010 次,CB 出现 99 次,09 出现 77 次,AC 出现 66 次,07 出现 55 次,101198 出现 44 次,011B6776FC 出现 33 次。

代码解析

参考代码

#include <iostream>
#include <string>
using namespace std;
struct int_16 {
string n_16;
int n = 0;
};
int char_to_int(char c) {
if ('0' <= c && c <= '9')
return c - '0';
return c - 'A' + 10;
}
char int_to_char(int a) {
if (a < 10)
return a + '0';
return a - 10 + 'A';
}
string dec_to_hex(int n) {
string s;
char c;
for (int i = 0; i < 2; i++) {
s = int_to_char(n % 16) + s;
n /= 16;
}
return s;
}
int hex_to_dec(char a, char b) {
return char_to_int(a) * 16 + char_to_int(b);
}
int main() {
int_16 cnt[256];
int pic[20][20] = {0};
int cnt_16[16] = {0};
int n;
string s = "";
cin >> n;
for (int i = 0; i < n; i++) {
string str;
cin >> str;
int j;
for (j = 0; str[2*j+1]; j++) {
int n_10 = 0;
n_10 = hex_to_dec(str[2*j], str[2*j+1]);
pic[i][j] = n_10;
if (!cnt[n_10].n)
cnt[n_10].n_16 = dec_to_hex(n_10);
cnt[n_10].n++;
}
pic[i][j+1] = -1;
}
for (int i = 0; i < 16; i++) {
for (int j = 255; j > i; j--) {
if (cnt[j].n > cnt[j-1].n)
swap(cnt[j], cnt[j-1]);
}
s += cnt[i].n_16;
cnt_16[i] = hex_to_dec(cnt[i].n_16[0], cnt[i].n_16[1]);
}
cout << s << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; pic[i][j+1] >= 0; j++) {
int min = abs(pic[i][j] - cnt_16[0]);
int idx = 0;
for (int k = 1; k < 16; k++) {
if (abs(pic[i][j] - cnt_16[k]) < min) {
min = abs(pic[i][j] - cnt_16[k]);
idx = k;
}
}
cout << int_to_char(idx);
}
cout << endl;
}
return 0;
}
GESP 2023年C++四级编程题解析
https://yezi.press/posts/gesp/gesp-cpp4-2023/
作者
Yezi 叶子
发布于
2023/12/01
许可协议
CC BY-NC-SA 4.0