中国电子学会(CEIT)考评中心历届真题(含解析答案)
C语言软件编程等级考试四级 2021年09月
编程题四道 总分:100分
一、吃奶酪(25分)
Jerry准备偷吃Tom的奶酪。所有的奶酪排成了一条直线,每块奶酪都有不同的美味程度。然而,如果有相邻两块奶酪都被Jerry偷吃,Tom就会发现这一点并迅速抓住Jerry。Jerry当然希望在不被Tom发现的条件下吃到的奶酪美味度总和最大。当然,他也可以选择一块奶酪都不吃。请你帮助他规划一下偷吃的方案,告诉他最多能偷吃到多少的美味度吧。
时间限制: 1000ms
内存限制: 65536kb
输入
第一行一个整数T(T<=100),表示测试数据组数。
接下来,每组测试数据包含两行。其中,第一行一个整数n (1<= n <= 100,000),表示奶酪的数量;第二行n个整数,表示这一排直线上奶酪的美味程度,请注意,美味度保证能够被int类型存储,且可能是负数。
输出
对于每组测试数据,输出一个整数,表示Jerry可以吃到的最大美味度总和。请注意,美味度总和可能超过int存储范围。
样例输入
2
4
1 2 3 1
5
2 7 9 3 1
样例输出
4
12
// 引入cstring库,用于处理字符串操作,这里主要是用于memset函数。
#include <cstring>
// 引入iostream库,用于输入输出操作。
#include <iostream>
// 使用std命名空间,避免在调用库函数时重复写std::。
using namespace std;// 定义两个全局数组d和a,分别用于存储动态规划的结果和输入的数值。
long long d[100005], a[100005];// 主函数入口。
int main() {// 定义变量t, n, c,分别用于存储测试用例的数量、每个测试用例的数字数量和结果的数量。int t, n, c = 0;// 定义数组r,用于存储每个测试用例的结果。long long r[100];// 从标准输入读取测试用例的数量。cin >> t;// 对于每个测试用例:while (t--) {// 使用memset函数初始化数组d和a为0。memset(d, 0,sizeof(d));memset(a, 0,sizeof(a));// 从标准输入读取数字的数量。cin >> n;// 对于每个数字:for (int i = 1; i <= n; i++)// 从标准输入读取该数字,并存储到数组a中。cin >> a[i];// 初始化动态规划的前两个值。d[1] = max(0,(int)a[1]);d[2] = max(int(d[1]), (int)a[2]);// 使用动态规划计算最大的非相邻数字之和。for (int i = 3; i <= n; i++){d[i]= max(d[i - 1], a[i] + d[i - 2]);}// 将当前测试用例的结果存储到数组r中。r[c++] = d[n];}// 输出每个测试用例的结果。for (int i = 0; i < c; i++)cout <<r[i]<< endl;// 程序结束,返回0。return 0;
}
/*这个程序的核心是使用动态规划来解决“打家劫舍”问题。它首先读取测试用例的数量,然后对于每个测试用例,读取数字的数量和具体的数字,然后使用动态规划计算最大的非相邻数字之和,并将结果存储起来。最后,它输出每个测试用例的结果。
*/
二、奶牛散步(25分)
从一个无限大的矩阵的中心点出发,一步只能向右走、向上走或向左走。恰好走N步且不经过已走的点共有多少种走法?
时间限制: 10000ms
内存限制: 131072kb
输入
一个数字,代表N,N <= 1000。输出
输出有多少方案。
样例输入
2
样例输出
7
#include <iostream> // 引入输入输出流库
using namespace std; // 使用标准命名空间int main() {int n, i = 0; // 定义整数n和i,并将i初始化为0cin >> n; // 从标准输入读取一个整数到nlong long lr = 2, up = 1; // 定义长整型变量lr和up,分别初始化为2和1long long t_lr = 0, t_up = 0; // 定义长整型变量t_lr和t_up,用于临时存储上一步的值if (n == 1){ // 如果n等于1cout << 3; // 输出3}else { // 否则for (i = 2; i <= n; i++){ // 从2循环到nt_lr = lr; // 将当前lr的值赋给t_lrt_up = up; // 将当前up的值赋给t_uplr = (t_up * 2 + t_lr) % 12345; // 更新lr的的值,根据规则计算并取模12345up = (t_up + t_lr) % 12345; // 更新up的值,根据规则计算并取模12345}cout << (up + lr) % 12345; // 输出最终up和lr的和,取模12345}return 0; // 程序正常结束,返回0
}
/*它读取一个整数n,并根据给定的规则计算出一系列的值,最后输出一个取模12345的结果。规则基于用户提供的移动方案:如果上一步向上走,下一步可以向左、右、上。如果上一步向左走,下一步可以向左、上。如果上一步向右走,下一步可以向右、上。程序首先检查n是否为1,如果是,则直接输出3。如果n大于1,程序会进入一个循环,在循环中,它会更新两个变量lr和up,这两个变量分别代表左右和上步的方向的累计值。在每次迭代中,t_lr和t_up被用来存储前一步的lr和up的值,然后根据规则更新lr和up。在循环结束后,程序输出up和lr的和,并取模12345。这个取模操作可能用于防止整数溢出,也可能是为了满足某种特定的输出要求。需要注意的是,此代码片段中的规则并没有在代码中明确表示出上一步的方向,而是通过变量lr和up的更新来隐含地表示。另外,初始值lr=2和up=1可能代表某种特定的初始状态或方向,但代码中并未给出具体的解释。
*/
三、数字构造(25分)
火山宝打算造一个n位的十进制数字出来。
对于1到n中的每一个i,火山宝可以从xi、1、…、xi、ki这ki个0-9的数字中选择一个作为ai。
在选择结束后,a1a…an形成了一个n位的十进制数,这就是火山宝造出来的数。你需要帮火山宝计算他能造出的数中,有多少个是3的倍数。
时间限制: 1000ms
内存限制: 65536kb
输入
第一行输入一个整数n ( 1=<n<=18),表示数字的位数。
接下来n行,每行第一个整数ki (1=<k<=10),表示第i中候选的数字数量。
接着是ki个两两不同的O-9范围内的数字xi、1、…、.xi、ki。输入保证O不是第一位的可选项。
输出
你需要输出一行一个整数,表示火山宝能造出的数字中,3的倍数的数量。样例输入
样例输入1:
2
5 5 6 7 8 9
5 0 1 2 3 4
样例输入2:
5
9 1 2 3 4 5 6 7 8 9
10 0 1 2 3 4 5 6 7 8 9
10 0 1 2 3 4 5 6 7 8 9
10 0 1 2 3 4 5 6 7 8 9
10 0 1 2 3 4 5 6 7 8 9
10 0 1 2 3 4 5 6 7 8 9
样例输出
样例输出1:
9
样例输出2:
30000
提示
样例1能造出来的3的倍数有51、54、60、63、72、81、84、90、93。
#include <cstring> // 引入cstring库,用于字符串处理,虽然在这段代码中并未使用到
#include <iostream> // 引入iostream库,用于输入输出操作
using namespace std; // 使用标准命名空间int k[20], num[20][10], mod[20][3]; // 声明三个全局数组,k用于存储序列长度,num用于存储序列中的数,mod用于统计模3的结果int main() { // 主函数开始int n; // 声明一个整型变量n,用于存储序列的数量cin >> n; // 从标准输入读取序列的数量for (int i = 0; i < n; i++){ // 遍历每个序列cin >> k[i]; // 读取当前序列的长度for (int j = 0; j < k[i]; j++){ // 遍历当前序列的每个数cin >> num[i][j]; // 读取当前数mod[i][num[i][j] % 3]++; // 统计当前数模3的结果,并对应位置计数加1}}long result[3]= {}; // 声明并初始化一个长整型数组result,用于存储最终的模运算结果// 初始化result为第一个序列的模3运算结果for (int i = 0; i < 3; i++)result[i] = mod[0][i];// 对每个序列进行模运算,并更新resultfor (int i = 1; i< n; i++){long temp0 = result[0], temp1 = result[1], temp2 = result[2]; // 保存result的当前值// 根据当前序列的模3运算结果更新resultresult[0] = temp0 * mod[i][0] + temp1 * mod[i][2] + temp2 * mod[i][1];result[1] = temp0 * mod[i][1] + temp1 * mod[i][0] + temp2 * mod[i][2];result[2] = temp0 * mod[i][2] + temp1 * mod[i][1] + temp2 * mod[i][0];}cout << result[0] << endl; // 输出最终的模运算结果return 0; // 程序结束
}
/*简单来说,这个程序是接收多个序列,每个序列包含若干个整数,然后对每个序列进行模3运算,并将每个序列的模运算结果累加到之前的结果上,最后输出最终的模运算结果。
*/
四、最佳路径(25分)
如下所示的由正整数数字构成的三角形:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的下边(正下方)的数或者右边(右下方)的数。
时间限制: 1000ms
内存限制: 65536kb
输入
第一行为三角形高度100>=h>=1,同时也是最底层边的数字的数目。从第二行开始,每行为三角形相应行的数字,中间用空格分隔。
输出
最佳路径的长度数值。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
提示:如何采用动态规划的思想,对问题进行分解。
#include<stdio.h> // 引入标准输入输出库// 定义二维数组dp,其中dp[i][j]表示从第i行第j列开始走到底层的最佳路径长度。
// 最终目标是计算dp[1][1],即从左上角开始走到底层的最佳路径长度。// 定义一个辅助函数max,用于返回两个整数中的较大值。
int max(int x, int y) {if (x > y)return x; // 如果x大于y,返回xreturn y; // 否则返回y
}int main() {int i, j, n; // 定义循环变量和楼层数nint arr[105][105], dp[105][105]; // 定义输入数组arr和动态规划数组dp,均为105x105大小// 读取楼层数nscanf("%d", &n);// 读取每一层的值到arr数组中for (i = 1; i <= n; ++i) {for (j = 1; j <= i; ++j) {scanf("%d", &arr[i][j]);}}// 初始化最后一层的dp值,因为从最后一层开始计算最佳路径for (i = 1; i <= n; ++i) {dp[n][i] = arr[n][i];}// 从倒数第二层开始,逐层向上计算dp数组的值for (i = n - 1; i >= 1; --i) {for (int j = 1; j <= i; ++j) {// 根据动态转移方程计算dp[i][j]的值dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + arr[i][j];}}// 输出最终结果,即dp[1][1]的值printf("%d", dp[1][1]);return 0; // 程序结束
}
/*这段代码实现了一个动态规划问题,通常被称为“最佳路径和”问题。给定一个由非负整数构成的楼梯形状数组arr,每个数字表示对应位置上的阶梯高度。从左上角(arr[1][1])开始走到右下角(arr[n][n]),每次只能向右或向下移动一步,求所有路径中最大的路径和。代码的主要逻辑如下:初始化输入数组arr和动态规划数组dp。读取楼梯的层数n和每一层的阶梯高度。初始化最后一层的dp值,因为从最后一层开始,路径只有一种选择。从倒数第二层开始,使用动态规划的方法逐层向上计算dp数组的值。输出左上角dp[1][1]的值,即从左上角到右下角的最佳路径和。
*/