中国电子学会(CEIT)考评中心历届真题(含解析答案)
C语言软件编程等级考试三级 2021年06月
编程题五道 总分:100分
一、数对(20分)
给定2到15个不同的正整数,你的任务是计算这些数里面有多少个数对满足:数对中一个数是另一个数的两倍。比如给定1 4 3 2 9 7 18 22,得到的答案是3,因为2是1的两倍,4是2个两倍,18是9的两倍。
时间限制: 1000ms
内存限制: 65536kb
输入
一行,给出2到15个两两不同且小于100的正整数。最后用O表示输入结束。
输出
一个整数,即有多少个数对满足其中一个数是另一个数的两倍。
样例输入
1 4 3 2 9 7 18 22 0
样例输出
3
#include<stdio.h> // 引入标准输入输出库
int main() {
int i,j,n; // 定义循环变量和有效数字个数n
int m[15],c=0; // 定义长度为15的数组m用于存储输入的数字,并初始化c为0(c用于统计满足条件的数字对数量)
// 输入数据,统计有效的数字个数n
for(i=0; i<15; i++){ // 遍历数组m的前15个元素
scanf("%d",&m[i]); // 从标准输入读取一个整数并存入m[i]
if(m[i]==0){ // 如果当前元素为0
n=i; // 则有效数字个数n为当前索引i
break; // 并跳出循环
}
}
// 枚举所有数据
for(i=0; i<n-1; i++){ // 外层循环遍历前n-1个有效数字
for(j=i+1; j<n; j++){ // 内层循环遍历从i+1到n-1的有效数字
int a=m[i]; // 将m[i]赋值给a
int b=m[j]; // 将m[j]赋值给b
// 保证a要比b大,用a/b
if(a<b){ // 如果a小于b
int t=a; // 交换a和b的值
a=b;
b=t;
}
// 判断a是b的两倍
if(a/b==2 && a%b==0) { // 如果a除以b的商为2且余数为0
c++; // 则满足条件的数字对数量c增加1
}
}
}
printf("%d",c); // 输出满足条件的数字对数量c
return 0; // 程序正常结束
}
/*
注意:
如果数组m中第一个元素就是0,那么n将被设置为0,
并且后面的循环将不会执行。这可能会导致程序逻辑错误。
在判断a/b==2 && a%b==0时,如果a和b都是非零整数,那么
a%b==0将始终为真,因为a是b的两倍。因此,a%b==0这个条
件实际上是多余的。
如果a和b都是负数,并且a是b的两倍,那么a/b可能会因为整
数除法而向下取整,导致a/b==2不成立。例如,如果a=-4,b=-2,
那么a/b将等于-2而不是2。这可能会导致一些满足条件的数字对被遗漏。
*/
二、井和绳子(20分)
有A,B, C,D, E五家人共用一口井,已知井深不超过k米。A,B, C , D, E的绳长各不相同,而且厘米表示的绳长一定是整数。
从井口放下绳索正好达到水面时:
(a)需要A家的绳n1条接上B家的绳1条
(b)需要B家的绳n2条接上C家的绳1条
©需要C家的绳n3条接上D家的绳1条
(d)需要D家的绳n4条接上E家的绳1条
(e)需要E家的绳n5条接上A家的绳1条
问井深和各家绳长。
时间限制: 1000ms
内存限制: 65536kb
输入
输入只有1行。包括空格分开的6个整数。第一个整数k (1 <= k <= 20),代表井的最大深度(单位:米)。接下来是5个正整数n1, n2,n3, n4, n5。这五个整数的含义见上面的题目描述。
输出
输出只有1行。如果找到了可行解,就输出6个整数,用空格分开,分别代表井的深度和A,B, C,D, E的绳长(单位都是厘米)。如果有多组可行解,输出井的深度最小的那组解。如果不存在可行解,就输出一行: not found
样例输入
10 2 3 4 5 6
样例输出
721 265 191 148 129 76
#include<stdio.h> // 引入标准输入输出库
int main() {
int i; //定义整型变量i
int k, len; // k表示井的深度(单位为米),len表示当前枚举的长度
int a, b, c, d, e; // a, b, c, d, e分别表示五种不同长度的绳子可以拉出的长度
int n[5]; // n数组存储五种不同绳子的拉伸倍数,例如n[0]表示第一种绳子的拉伸倍数
// 读取用户输入的井的深度(单位为米)
scanf("%d" ,&k);
k *= 100; // 将井的深度转换为厘米,因为绳子拉伸的倍数是以厘米为单位的
// 读取五种绳子的拉伸倍数
for(i = 0; i < 5; i++){
scanf("%d", &n[i]);
}
// 使用枚举法枚举井的深度
for(len = 1; len <= k; len++){ // 枚举a的长度,从1开始到井的最大深度
for(a = 1; a < len - 1; a++){ // 枚举a的长度,注意a不能是0或len-1,因为b和e的计算需要用到len-a和len-d
b = len - n[0] * a; // 根据第一种绳子的拉伸倍数和a的长度,计算b的长度
c = len - n[1] * b; // 根据第二种绳子的拉伸倍数和b的长度,计算c的长度
d = len - n[2] * c; // 根据第三种绳子的拉伸倍数和c的长度,计算d的长度
e = len - n[3] * d; // 根据第四种绳子的拉伸倍数和d的长度,计算e的长度
// 检查是否满足条件:e*n[4] + a == len,即最后一段绳子(e)拉伸后加上第一段绳子(a)的长度等于井的总深度
if(e * n[4] + a == len) {
printf("%d %d %d %d %d %d" ,len, a, b, c, d, e); // 如果满足条件,输出结果
return 0; // 找到解后退出程序
}
}
}
printf("not found"); // 如果没有找到满足条件的解,输出"not found"
return 0; // 程序结束
}
/*
该代码的目的是通过五种不同拉伸倍数的绳子来测量一个井的深度。
它使用枚举法来尝试所有可能的绳子长度组合,以找到一种组合,
使得这五种绳子拉伸后的总长度等于井的深度。如果找到了这样的
组合,就输出这个组合;否则,输出"not found"。
*/
三、爬楼(20分)
已知楼梯的数量,可以每次走2级或者3级,求不同的走法数
例如:楼梯一共有7级,一共3种方法: 2 2 3或者 2 3 2或者 3 2 2。
时间限制: 1000ms
内存限制: 65536kb
输入
输入包含若干行,每行包含一个正整数N,代表楼梯级数,1 <=N <= 50。最后一行为0,表示测试结束。
输出
不同的走法数,每一行输入对应一行输出
样例输入
7
0
样例输出
3
#include <stdio.h> // 引入标准输入输出库,用于scanf和printf函数
// 定义一个函数calculate,用于计算上楼梯的方式数量
int calculate(int n) {
// 如果只有一个楼梯,那么只有一种方式:不爬
if (n == 1)
return 0;
// 如果有两个楼梯,有两种方式:爬一个楼梯然后跳一个,或者直接跳两个
else if (n == 2)
return 1;
// 如果有三个楼梯,只有一种方式:爬一个楼梯然后跳两个
else if(n == 3)
return 1;
// 对于其他数量的楼梯,使用递归方式计算
// 上n个楼梯的方式数等于上n-2个楼梯的方式数(跨过一个楼梯)加上上n-3个楼梯的方式数(跨过两个楼梯)
else
return calculate(n - 2) + calculate(n - 3);
}
// main函数,程序的入口点
int main() {
int n; // 定义一个整数变量n,用于存储用户输入的楼梯数量
int c; // 定义一个整数变量c,用于存储计算出的上楼梯的方式数量
// 使用一个无限循环,直到用户输入0为止
while(1) {
scanf("%d",&n); // 从标准输入读取一个整数,并存储在变量n中
if(n==0) // 如果用户输入的是0,则跳出循环
break;
c=calculate(n); // 调用calculate函数计算上n个楼梯的方式数量,并将结果存储在变量c中
printf("%d\n",c); // 将计算结果输出到标准输出
}
return 0; // 程序正常结束,返回0
}
/*
这个代码实现了一个递归函数calculate,用于计算上n个楼梯的不同方式数量。
根据题目描述,每次可以爬1个或2个楼梯,因此上n个楼梯的方式数等于上n-2个
楼梯的方式数(跨过一个楼梯)加上上n-3个楼梯的方式数(跨过两个楼梯)。
注意,这个递归实现方式在n较大时可能会导致性能问题,因为它会重复计算很
多子问题。在实际应用中,可以考虑使用动态规划或其他优化方法来提高性能。
*/
四、表达式求值(20分)
输入一个布尔表达式,请你输出它的真假值。比如:(V|V )&F & ( F|V)
V表示true,F表示false,&表示与,|表示或,!表示非。上式的结果是F
时间限制: 1000
内存限制: 65536
输入
输入包含多行,每行一个布尔表达式,表达式中可以有空格,总长度不超过1000
输出
对每行输入,如果表达式为真,输出"V",否则出来"F"
样例输入
(V |V )&F &( F| V)
!V |V &V & !F & (F|V )&(!F|F | !V & V)
(F&F|V|!V&!F&!(F|F&V))
样例输出
F
v
v
#include<iostream> // 引入输入输出流库
#include <cstdio> // 引入C标准输入输出库
#include<algorithm> // 引入算法库(这里虽然没有直接使用到算法库的功能)
#include<queue> // 引入队列容器
#include <stack> // 引入栈容器
#include<string> // 引入字符串处理库
using namespace std; // 使用标准命名空间
// 定义一个函数来计算两个字符('V' 或 'F')之间的逻辑运算
char calculate(char x, char y, char oper){
bool a = (x == 'V'), b = (y == 'V'), ans;
if (oper == '|') ans = (a || b); // 或运算
else if (oper == '&') ans = (a && b); // 与运算
return ans ? 'V' : 'F'; // 返回结果,'V' 代表真,'F' 代表假
}
// 定义一个函数来反转字符 'V' 和 'F'
char reverse(char x){
if (x == 'V') return 'F';
return 'V';
}
int main() {
string in;
int i, j, len;
while (getline(cin, in)) { // 读取输入的表达式
stack< char > Oper, num; // Oper 保存运算符,num 保存运算结果
queue< char > s; // s 是前缀表达式
len = in.length();
i = len;
in = " " + in; // 在字符串前面加一个空格,便于处理
// 从右往左遍历,将中缀表达式转换为前缀表达式
while (i > 0){
if (in[i] == ' '){
i--;
continue;
} else if (isalpha(in[i])) s.push(in[i--]); // 如果是字母,则直接加入前缀表达式
else if (in[i] == '!') s.push(in[i--]); // 单目运算符 '!' 直接加入前缀表达式
else {
if (in[i] == '&' || in[i] == '|' || in[i] == ')'){ // 如果是低级运算符或右括号
Oper.push(in[i--]); // 压入运算符栈
} else if (in[i] == '('){ // 如果是左括号
while (Oper.top() != ')'){ // 弹出直到遇到右括号
s.push(Oper.top());
Oper.pop();
}
Oper.pop(); // 弹出右括号
i--;
}
}
}
// 将运算符栈中剩余的运算符弹出并加入前缀表达式
while (!Oper.empty()) s.push(Oper.top()), Oper.pop();
// 计算前缀表达式
while (!s.empty()){
char ch = s.front();
s.pop();
if (isalpha(ch)) num.push(ch); // 如果是字母,则加入运算结果栈
else Oper.push(ch); // 如果是运算符,则压入运算符栈
// 如果是单目运算符 '!'
if (!num.empty() && !Oper.empty() && Oper.top() == '!'){
char x = num.top();
num.pop();
Oper.pop();
num.push(reverse(x)); // 对结果进行反转
}
// 如果是双目运算符
else if (num.size() >= 2 && !Oper.empty()){
char oper = Oper.top(), x, y;
Oper.pop();
x = num.top();
num.pop();
y = num.top();
num.pop();
num.push(calculate(x, y, oper)); // 进行计算并将结果压入运算结果栈
}
}
cout << num.top() << endl; // 输出最终的计算结果
}
}
/*
这个代码的核心思路是:
从右往左遍历中缀表达式,将其转换为前缀表达式。
使用栈来保存运算符,以便处理运算符的优先级。
计算前缀表达式,得到最终结果。
这段代码的主要功能是将中缀表达式转换为前缀表达式,
并计算前缀表达式的值。中缀表达式是我们常见的数学表达式形式,
例如A & (B | C)。前缀表达式(也称为波兰表示法)是一种将运算
符放在操作数前面的形式,例如& A | B C。这段代码首先使用中缀
表达式构建前缀表达式,然后计算前缀表达式的值。
*/
五、数列(20分)
用以下方式构造数列:数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。
给出一个正整数a,要求数列中第a个数对1000取模的结果是多少。
时间限制: 1000ms
内存限制: 65536kb
输入
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a(1<=a <= 1000000)。
输出
n行,每行输出对应一个输入。输出应是一个正整数,为数列中第a个数对1000取模得到的结果。
样例输入
4
5
2
19
1
样例输出
5
1
181
1
#include<stdio.h> // 引入标准输入输出库
// 计算斐波那契数列的第n项
long long fun(int n) {
if(n == 1) // 如果n等于1,返回斐波那契数列的第1项,值为1
return 1;
if(n == 2) // 如果n等于2,返回斐波那契数列的第2项,值也为1
return 1;
return fun(n-1) + fun(n-2); // 递归地计算斐波那契数列的第n-1项和第n-2项,并返回它们的和
}
int a[1000]; // 定义一个整数数组a,用于存储用户输入的数值
int main() {
int i, n; // 定义循环变量i和用于存储用户输入数量的变量n
scanf("%d", &n); // 从标准输入读取一个整数,并存储在变量n中
for(i = 0; i < n; i++) { // 使用循环读取用户输入的n个整数
scanf("%d", &a[i]); // 从标准输入读取一个整数,并存储在数组a的第i个位置
}
for(i = 0; i < n; i++) { // 使用循环计算并输出每个输入数字对应的斐波那契数列项的模1000结果
printf("%d\n", fun(a[i]) % 1000); // 计算斐波那契数列的第a[i]项,并取其对1000的模,然后输出结果
}
return 0; // 程序正常结束,返回0
}