4101 字
21 分钟
GESP 2024年C++四级编程题解析
2024/03/19
2024/12/07

202412-1 Recamán#

题目描述

小杨最近发现了有趣的 Recamán 数列,这个数列是这样生成的:

  • 数列的第一项 a1a_111
  • 如果 ak1ka_{k-1}-k 是正整数并且没有在数列中出现过,那么数列的第 kkaka_kak1ka_{k-1}-k,否则为 ak1+ka_{k-1}+k

小杨想知道 Recamán 数列的前 nn 项从小到大排序后的结果。手动计算非常困难,小杨希望你能帮他解决这个问题。

输入格式

第一行,一个正整数 nn

输出格式

一行,nn 个空格分隔的整数,表示 Recamán 数列的前 nn 项从小到大排序后的结果。

样例输入 1

5

样例输出 1

1 2 3 6 7

样例输入 2

8

样例输出 2

1 2 3 6 7 12 13 20

提示

【样例解释】

对于样例 1,n=5n=5

  • a1=1a_1=1
  • a12=1a_1-2=-1,不是正整数,因此 a2=a1+2=3a_2=a_1+2=3
  • a23=0a_2-3=0,不是正整数,因此 a3=a2+3=6a_3=a_2+3=6
  • a34=2a_3-4=2,是正整数,且没有在数列中出现过,因此 a4=a34=2a_4=a_3-4=2
  • a45=3a_4-5=-3,不是正整数,因此 a5=a4+5=7a_5=a_4+5=7

a1,a2,a3,a4,a5a_1,a_2,a_3,a_4,a_5 从小到大排序的结果为 1,2,3,6,71,2,3,6,7

【数据范围】

对于所有数据点,保证 1n30001\le n\le 3\, 000

代码解析

根据题目意思写代码,模拟法解题即可,这里可以把“正整数并且没有在数列中出现过”封装为一个 check() 函数,可以让主函数可读性更佳,其余没有什么难点了。

#include<bits/stdc++.h>
using namespace std;
int n, a[3005];
bool check(int num) {
if (num <= 0) return false;
for (int i = 1; i <= n; i++)
if (num == a[i])
return false;
return true;
}
int main() {
a[1] = 1;
cin >> n;
for (int i = 2; i <= n; i++)
if (check(a[i-1] - i))
a[i] = a[i-1] - i;
else
a[i] = a[i-1] + i;
sort(a+1, a+n+1);
for (int i = 1; i <= n ; i++)
cout << a[i] << ' ';
return 0;
}

202412-2 字符排序#

题目描述

小杨有 nn 个仅包含小写字母的字符串 s1,s2,,sns_1,s_2,\ldots,s_n,小杨想将这些字符串按一定顺序排列后拼接到一起构成字符串 tt。小杨希望最后构成的字符串 tt 满足:

  • 假设 tit_i 为字符串 tt 的第 ii 个字符,对于所有的 j<ij\lt i 均有 tjtit_j\le t_i。两个字符的大小关系与其在字母表中的顺序一致,例如 e<g<p<s\texttt{e}\lt \texttt{g}\lt \texttt{p} \lt \texttt{s}

小杨想知道是否存在满足条件的字符串排列顺序。

输入格式

第一行包含一个正整数 TT,代表测试数据组数。

对于每组测试数据,第一行包含一个正整数 nn,含义如题面所示。

之后 nn 行,每行包含一个字符串 sis_i

输出格式

对于每组测试数据,如果存在满足条件的排列顺序,输出(一行一个)1\texttt{1},否则输出(一行一个) 0\texttt{0}

样例输入 1

3
3
aa
ac
de
2
aac
bc
1
gesp

样例输出 1

1
0
0

提示

【样例解释】

对于第一组测试数据,一种可行的排列顺序为 aa+ac+de\texttt{aa}+\texttt{ac}+\texttt{de},构成的字符串 ttaaacde\texttt{aaacde},满足条件。

对于全部数据,保证有 1T,n1001\le T,n\le 100,每个字符串的长度不超过 1010

代码解析

题目的意思是,我们需要对于这个字符串数组重新排列,拼接成一个新的字符串,这个字符串里面的字符都需要按照字母表(ASCII 编码)去排序,需要知道能否找到这样的排列方式

思考一下,如果存在这样的排列方式,那么任何一个字符串的字典序一定比前一个字符串的字典序大,所以我们可以先对字符串数组按照字典序排序,然后拼接成新字符串,检查一下拼接后的字符串的每个字符是否大于等于前一个字符即可。

#include<bits/stdc++.h>
using namespace std;
bool check(string s) {
for (int i = 1; i < s.size(); i++)
if (s[i-1] > s[i])
return false;
return true;
}
int main() {
int t, n;
cin >> t;
while (t--) {
cin >> n;
string a[105], s;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a+n);
for (int i = 0; i < n; i++)
s += a[i];
if (check(s)) cout << 1 << endl;
else cout << 0 << endl;
}
return 0;
}

202409-1 黑白方块#

题目描述

小杨有一个 nnmm 列的网格图,其中每个格子要么是白色,要么是黑色。 小杨想知道网格图中是否存在一个满足如下条件的子矩形:

  • 子矩形由 4444 列组成;
  • 子矩形的第 11 行和第 44 行只包含白色格子;
  • 对于子矩形的第 22 行和第 33 行,只有第 11 个和第 44 个格子是白色的,其余格子都是黑色的;

请你编写程序帮助小杨判断。

输入格式

第一行包含一个正整数 tt,代表测试用例组数。
接下来是 tt 组测试用例。对于每组测试用例,一共 n+1n+1 行。
第一行包含两个正整数 n,mn,m,含义如题面所示。
之后 nn 行,每行一个长度为 mm0101 串,代表网格图第 ii 行格子的颜色,如果为 00,则对应格子为白色,否则为黑色。

输出格式

对于每组测试用例,如果存在,输出 Yes,否则输出 No。

样例输入 1

3
1 4
0110
5 5
00000
01100
01100
00001
01100
5 5
00000
01100
01110
00001
01100

样例输出 1

No
Yes
No

提示

【样例 1 解释】

0000
0110
0110
0000

【数据规模与约定】

对全部的测试数据,保证 1t101 \leq t\leq 101n,m1001 \leq n,m \leq 100

代码解析

这道题也是暴力枚举解题即可,注意有多层循环跳出比较麻烦的话可以多封装几个函数,模块化编写不容易出问题,这道题需要注意的是行列如果小于 4 则不需要进行额外判断,还有多个 if 如果嵌套的话注意加大括号。

#include<bits/stdc++.h>
using namespace std;
int t, n, m;
int a[105][105];
bool check4(int i, int j) {
for (int x = i; x <= i+3; x++)
for (int y = j; y <= j+3; y++)
if (x == i || x == i+3) {
if (a[x][y] == 1) return false;
} else {
if (y == j || y == j+3) {
if (a[x][y] == 1) return false;
} else {
if (a[x][y] == 0) return false;
}
}
return true;
}
bool check() {
if (n < 4 || m < 4) return false;
// 枚举 4 行 4 列的左上角起点
for (int i = 1; i <= n-3; i++)
for (int j = 1; j <= m-3; j++)
if (check4(i, j))
return true;
return false;
}
int main() {
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
for (int j = 1; j <= m; j++)
a[i][j] = s[j-1] - '0';
}
if (check())
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}

202409-2 区间排序#

题目描述

小杨有一个包含 nn 个正整数的序列 aa

小杨计划对序列进行多次升序排序,每次升序排序小杨会选择一个区间 [l,r][l,r]lrl \leq r)并对区间内所有数字,即进行升序 al,al+1,ara_l, a_{l + 1}, \dots a_r 排序。每次升序排序会在上一次升序排序的结果上进行。

小杨想请你计算出多次升序排序后的序列。

输入格式

第一行包含一个正整数 nn,含义如题面所示。
第二行包含 nn 个正整数 a1,a2,ana_1, a_2, \dots a_n,代表序列 aa
第三行包含一个正整数 qq,代表排序次数。
之后 qq 行,每行包含两个正整数 l,rl, r,代表将区间 [li,ri][l_i, r_i] 内所有数字进行升序排序。

输出格式

输出一行包含 nn 个正整数,代表多次升序排序后的序列。

样例输入 1

5
3 4 5 2 1
3
4 5
3 4
1 3

样例输出 1

1 3 4 5 2

提示

【样例 1 解释】

  • 第一次升序排序后,序列为 [3,4,5,1,2][3,4,5,1,2]
  • 第二次升序排序后,序列为 [3,4,1,5,2][3,4,1,5,2]
  • 第三次升序排序后,序列为 [1,3,4,5,2][1,3,4,5,2]

【数据规模与约定】

对于全部的测试数据,保证 1n,ai,q1001 \leq n, a_i, q \leq 1001lirin1 \leq l_i \leq r_i \leq n

代码解析

很简单的一道题,考察对于 STL sort() 的运用,注意参数即可。

#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 q, l, r;
cin >> q;
while (q--) {
cin >> l >> r;
sort(a+l, a+r+1);
}
for (int i = 1; i <= n; i++)
cout << a[i] << ' ';
return 0;
}

202406-1 黑白方块#

题目描述

小杨有一个 nnmm 列的网格图,其中每个格子要么是白色,要么是黑色。对于网格图中的一个子矩形,小杨认为它是平衡的当且仅当其中黑色格子与白色格子数量相同。小杨想知道最大的平衡子矩形包含了多少个格子。

输入格式

第一行包含两个正整数 n,mn,m,含义如题面所示。

之后 nn 行,每行一个长度为 mm0101 串,代表网格图第 ii 行格子的颜色,如果为 00,则对应格子为白色,否则为黑色。

输出格式

输出一个整数,代表最大的平衡子矩形包含格子的数量,如果不存在则输出 00

样例输入 1

4 5
00000
01111
00011
00011

样例输出 1

16

提示

【样例解释】

对于样例 11,假设 (i,j)(i,j) 代表第 ii 行第 jj 列,最大的平衡子矩形的四个顶点分别为 (1,2),(1,5),(4,2),(4,5)(1,2),(1,5),(4,2),(4,5)

【数据范围】

对于全部数据,保证有 1n,m101\leq n,m\leq 10

代码解析

暴力枚举解题即可,我们枚举子矩形的左上角的坐标(a, b)以及右下角的坐标(c, d),检查这个矩形中黑白方块的个数是否相同即可,这道题考察的依旧是对题目的理解能力、数组的下标规律以及编写代码的细心程度。

#include<bits/stdc++.h>
using namespace std;
int n, m, max_cnt = 0;
char w[11][11];
void check(int a, int b, int c, int d) {
int t = 0, f = 0;
for (int i = a; i <= c; i++)
for (int j = b; j <= d; j++)
if (w[i][j] == '1') t++;
else f++;
if (t == f) {
max_cnt = max(max_cnt, (c-a+1)*(d-b+1));
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
for (int a = 1; a <= n; a++)
for (int b = 1; b <= m; b++)
for (int c = a; c <= n; c++)
for (int d = b; d <= m; d++)
check(a, b, c, d);
cout << max_cnt;
return 0;
}

202406-2 宝箱#

题目描述

小杨发现了 nn 个宝箱,其中第 ii 个宝箱的价值是 aia_i

小杨可以选择一些宝箱放入背包并带走,但是小杨的背包比较特殊,假设小杨选择的宝箱中最大价值为 xx,最小价值为 yy,小杨需要保证 xykx-y\leq k,否则小杨的背包会损坏。

小杨想知道背包不损坏的情况下,自己能够带走宝箱的总价值最大是多少。

输入格式

第一行包含两个正整数 n,kn,k,含义如题面所示。

第二行包含 nn 个正整数 a1,a2,,ana_1,a_2,\dots,a_n,代表宝箱的价值。

输出格式

输出一个整数,代表带走宝箱的最大总价值。

样例输入 1

5 1
1 2 3 1 2

样例输出 1

7

提示

【样例解释】

在背包不损坏的情况下,小杨可以拿走两个价值为 22 的宝箱和一个价值为 33 的宝箱。

【数据范围】

对于全部数据,保证有 1n10001\leq n\leq 10000k10000\leq k\leq 10001ai10001\leq a_i\leq 1000

代码解析

还是枚举啦,小杨的背包中最大价值和最小价值的物品的差是有要求的,我们只需要把物品按照价值从小到大排序,然后确定一个价值最小的物品 a[i],然后累加 a[i] 之后所有满足 a[x] - a[i] <= ka[x],就可以枚举出一种可能性,再打擂台即可。

#include<bits/stdc++.h>
using namespace std;
int main() {
int a[1005], n, k, w = 0;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a+n);
for (int i = 0; i < n; i++)
for (int j = i; j < n; j++){
int sum = 0;
for (int x = i; x <= j; x++)
if (a[x] - a[i] <= k)
sum += a[x];
w = max(sum, w);
}
cout << w;
return 0;
}

如果觉得三层循环枚举时间复杂度太高,也可以使用双指针,如果 a[x] - a[i] <= kx++,否则 i++。记得同步修改 sum,注意边界问题。

#include<bits/stdc++.h>
using namespace std;
int main() {
int a[1005], n, k, w = 0;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a+n);
int i = 0, x = 0, sum = 0;
while (x != n) {
while (a[x] - a[i] <= k && x < n) {
sum += a[x];
x++;
}
w = max(sum, w);
while (a[x] - a[i] > k && i <= x) {
sum -= a[i];
i++;
}
}
cout << w;
return 0;
}

202403-1 相似字符串#

题目描述

对于两个字符串 AABB,如果 AA 可以通过删除一个字符,插入一个字符,修改一个字符变成 BB,那么我们说 AABB 是相似的。

比如 apple\texttt{apple} 可以通过插入一个字符变成 applee\texttt{applee},可以通过删除一个字符变成 appe\texttt{appe},也可以通过修改一个字符变成 bpple\texttt{bpple}。因此 apple\texttt{apple}applee\texttt{applee}appe\texttt{appe}bpple\texttt{bpple} 都是相似的。但 applee\texttt{applee} 并不能 通过任意一个操作变成 bpple\texttt{bpple},因此它们并不相似。

特别地,两个完全相同的字符串也是相似的。

给定 TTA,BA,B,请你分别判断它们是否相似。

输入格式

第一行一个正整数 TT
接下来 TT 行,每行两个用空格隔开的字符串 AABB

输出格式

对组 A,BA,B,如果他们相似,输出 similar,否则输出 not similar

样例输入 1

5
apple applee
apple appe
apple bpple
applee bpple
apple apple

样例输出 1

similar
similar
similar
not similar
similar

提示

对全部的测试数据,保证 1T1001 \leq T \leq 100AABB 的长度不超过 5050,仅含小写字母。

代码解析

题目本身不难,判断需要先看两个字符之间的长度差是 0 还是 1,除他们之外就不可能相似,分情况讨论:

  • 如果长度差是 0,则只允许一个字符不相同
  • 如果长度差是 1,则肯定是多了一位,其他位都相同
#include<bits/stdc++.h>
using namespace std;
bool is_similar(string s1, string s2) {
// 为了方便处理,我们通过交换确保 s1 比 s2 长
if (s1.size() > s2.size()) swap(s1, s2);
int sub_size = s2.size() - s1.size();
if (sub_size == 0) {
int cnt = 0;
for (int i = 0; i < s1.size(); i++)
if (s1[i] != s2[i])
cnt++;
return cnt <= 1;
} else if (sub_size == 1) {
for (int i = 0, j = 0; i < s1.size(); i++, j++)
if (s1[i] != s2[j]) {
j++;
if (s1[i] != s2[j]) return false;
}
return true;
}
return false;
}
int main() {
int n;
cin >> n;
string s1, s2;
while (n--) {
cin >> s1 >> s2;
if (is_similar(s1, s2))
cout << "similar" << endl;
else
cout << "not similar" << endl;
}
return 0;
}

202403-2 做题#

题目描述

小杨同学为了提高自己的实力制定了做题计划,在第 kk 天时,他必须要完成 kk 道题,否则他就会偷懒。

小杨同学现在找到了一个题库,一共有 nn 套题单,每一套题单中有一定数量的题目。但是他十分挑剔,每套题单他只会使用一次,每一天也只能使用一套题单里的题目,之后那套题单就会弃之不用。对于每套题单,他不必完成题单内所有的题。

那么问题来了,小杨同学最多做题几天才偷懒呢?

输入格式

第一行,一个整数为 nn,表示有多少套题单。
第二行 nn 个整数 a1,a2,ana_1, a_2, \dots a_n,分别表示每套题单有多少道题。

输出格式

输出一行一个整数表示答案。

样例输入 1

4
3 1 4 1

样例输出 1

3

提示

对全部的测试数据,保证 1n1061 \leq n \leq 10^61ai1091 \leq a_i \leq 10^9

代码解析

认真细心即可答对,考点就是数组以及排序,其实也可以说是双指针思路,排好序之后依次尝试,如果卷子 a[i] 的题目数量大于天数 day 则换下一套题下一天,如果题目不够则只换下一套题(i++),最后注意如果最后一天题目不够的话,天数实际上是多一天。

#include<bits/stdc++.h>
using namespace std;
int main() {
int n, a[1000005], day = 1;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a+n);
for (int i = 0; i < n; i++)
if (a[i] >= day)
day++;
cout << day-1;
return 0;
}
GESP 2024年C++四级编程题解析
https://yezi.press/posts/gesp/gesp-cpp4-2024/
作者
Yezi 叶子
发布于
2024/03/19
许可协议
CC BY-NC-SA 4.0