2023 第一轮真题解析(二):阅读程序题
2023 年 CCF 非专业级软件能力认证(CSP-J/S 2023)第一轮认证于 2023 年 9 月 16 日举行。继上一篇单项选择题解析后,本文为您带来 第二部分:阅读程序题(共 3 大题,计 40 分) 的逐题源码分析、算法推演与深度全解析。
阅读程序题主要考查对 C++ 格式化输出、海伦公式、动态规划最长公共子序列(LCS)、字符串匹配与移位、试除法分解约数及数论性质 等知识点的掌握。
📌 二、阅读程序(程序输入不超过数组或字符串定义的范围;判断题正确填 √,错误填 ×;除特殊说明外,判断题 1.5 分,选择题 3 分,共计 40 分)
📍 第一题:海伦公式计算三角形面积与格式化输出
💻 源码展示
#include<iostream>
#include<cmath>
using namespace std;
double f(double a,double b,double c){
double s=(a+b+c)/2;
return sqrt(s*(s-a)*(s-b)*(s-c));
}
int main(){
cout.flags(ios::fixed);
cout.precision(4);
int a,b,c;
cin>>a>>b>>c;
cout<<f(a,b,c)<<endl;
return 0;
}假设输入的所有数都是不超过 1000 的正整数,完成下面的判断题和单选题:
💡 程序主旨
本程序核心在于通过 海伦公式(Heron’s Formula) 计算边长分别为 的三角形面积:
f(a, b, c)函数:先计算半周长 ,随后使用公式 计算并返回三角形面积。main函数:cout.flags(ios::fixed)与cout.precision(4)组合使用,控制标准输出流以 定点小数格式(fixed-point) 精确保留 4 位小数。- 读取三个正整数 ,调用
f(a,b,c)并输出计算结果。
❓ 判断题
1. (2分)当输入为 2 2 2 时,输出为 1.7321 ( )
A. 正确
B. 错误
正确答案: A
深度解析:
当 时,该三角形为边长为 2 的等边三角形。
- 半周长 。
- 面积
- 由于程序指定保留 4 位小数,对 进行四舍五入后结果为
1.7321。 因此本题说法正确。
2. (2分)将第 7 行中的 (s-b)*(s-c) 改为 (s-c)*(s-b) 不会影响程序运行的结果 ( )
A. 正确
B. 错误
正确答案: A
深度解析:
在实数乘法运算中,乘法满足 交换律,即对任意实数 ,均有 。
因此,将 (s-b)*(s-c) 改写为 (s-c)*(s-b) 表达式的代数含义和数值计算结果完全一致,不会对程序运行结果产生任何影响。故本题说法正确。
3. (2分)程序总是输出四位小数 ( )
A. 正确
B. 错误
正确答案: A
深度解析:
在 C++ iostream 中:
cout.flags(ios::fixed)强制浮点数采用定点小数表示法输出;cout.precision(4)设置浮点数的显示精度为 4 位。 两者结合后,即使计算结果为整数(如 6 或 30),cout也会在末尾自动补零输出6.0000或30.0000。由于题目假设输入的数均为不超过 1000 的正整数,输出始终为浮点数且固定展示 4 位小数。因此本题说法正确。
❓ 单选题
4. 当输入为 3 4 5 时,输出为 ( )
A.
6.0000
B.12.0000
C.24.0000
D.30.0000
正确答案: A
深度解析:
是一组经典的勾股数,构成的三角形为直角三角形(直角边长为 3 和 4)。
- 面积计算:。
- 海伦公式校验:,。
配合 4 位小数的格式化输出,最终输出结果为
6.0000。
5. 当输入为 5 12 13 时,输出为 ( )
A.
24.0000
B.30.0000
C.60.0000
D.120.0000
正确答案: B
深度解析:
同样是一组标准的勾股数(),构成的三角形为直角三角形(直角边长为 5 和 12)。
- 面积计算:。
- 海伦公式校验:,。
配合 4 位小数格式化输出,结果为
30.0000,对应 B 选项。
📍 第二题:最长公共子序列(LCS)与字符串循环匹配
💻 源码展示
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int f(string x,string y){
int m=x.size();
int n=y.size();
vector<vector<int>>v(m+1,vector<int>(n+1,0));
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(x[i-1]==y[j-1]){
v[i][j]=v[i-1][j-1]+1;
}else{
v[i][j]=max(v[i-1][j],v[i][j-1]);
}
}
}
return v[m][n];
}
bool g(string x,string y){
if(x.size() != y.size()){
return false;
}
return f(x+x,y)==y.size();
}
int main(){
string x,y;
cin>>x>>y;
cout<<g(x,y)<<endl;
return 0;
}💡 程序主旨
f(x, y)函数:这是求解 最长公共子序列(Longest Common Subsequence, LCS) 的经典动态规划算法。m和n分别为字符串x和y的长度。- 二维数组
v[i][j]表示x[0...i-1]与y[0...j-1]的最长公共子序列长度。 - 若
x[i-1] == y[j-1],则v[i][j] = v[i-1][j-1] + 1;否则v[i][j] = max(v[i-1][j], v[i][j-1])。 - 函数最终返回
v[m][n],即x和y的 LCS 长度。
g(x, y)函数:- 首先校验
x与y长度是否相等,若不等直接返回false。 - 若长度相等,则计算
f(x+x, y)。将x拼接自身得到x+x(长度为 ),若y是x+x的一个子序列且长度等于y.size(),则返回true。
- 首先校验
❓ 判断题
1. f 函数的返回值小于等于 。( )
A. 正确
B. 错误
正确答案: A
深度解析:f(x, y) 计算的是字符串 x(长度 )和 y(长度 )的 最长公共子序列长度。
公共子序列中的字符必定来自原字符串,其长度不可能超过参与比较的任何一个字符串的长度。因此 LCS 长度的上界为 ,本题说法正确。
2. f 函数的返回值等于两个输入字符串的最长公共子串的长度。( )
A. 正确
B. 错误
正确答案: B
深度解析:
在数据结构与算法中:
- 最长公共子串(Longest Common Substring) 要求字符在原字符串中必须 连续;
- 最长公共子序列(Longest Common Subsequence, LCS) 只要求字符保持 相对顺序,不要求连续。
代码中当
x[i-1] != y[j-1]时,使用了v[i][j] = max(v[i-1][j], v[i][j-1])继承前面的历史匹配状态,这正是 最长公共子序列 的状态转移方程。因此说法错误。
3. 当输入两个完全相同的字符串时,g 函数的返回值总是 true。( )
A. 正确
B. 错误
正确答案: A
深度解析:
当输入 与 完全相同(即 且 )时:
- 满足,不会提前返回
false。 - 包含了完整的原字符串 作为其前半部分前缀。
- 因为 ,所以 显然是 的一个子序列,因此 。
f(x+x, y) == y.size()恒成立,g函数必然返回true。故本题说法正确。
❓ 单选题
4. 将第 19 行中的 v[m][n] 替换为 v[n][m],那么该程序( )。
A. 行为不变
B. 只会改变输出
C. 一定异常退出
D. 可能异常退出
正确答案: D
深度解析:
在 f(x, y) 函数内部,二维动态数组 v 声明大小为 v(m+1, vector<int>(n+1, 0)),即共有 行、 列:
- 第一维(行)合法下标范围为 ;
- 第二维(列)合法下标范围为 。
当我们将返回值改为 v[n][m] 时:
- 情况一():第一维下标 超过了数组行数上界 ,发生 越界访问(Out of Bounds),极易触发 Segmentation Fault / 内存非法访问,导致程序 异常退出。
- 情况二():第一维下标未越界,程序不会崩溃,但访问了错误的位置(
v[n][m]而非答案所在的v[m][n]),导致输出错误答案。
综合以上两种情况,该程序表现为 “可能异常退出”,故选 D。
5. 当输入为 csp-j p-jcs 时,输出为( )。
A.
0
B.1
C.T
D.F
正确答案: B
深度解析:
- 参数分析:
"csp-j"(长度 5),"p-jcs"(长度 5)。长度相等。 - 拼接字符串:
"csp-jcsp-j"。 - 匹配子序列:我们在
"csp-jcsp-j"中寻找"p-jcs"的每个字符:- 第 1 个字符
'p':出现在x+x下标 2 处(cs[p]-jcsp-j) - 第 2 个字符
'-':出现在x+x下标 3 处(csp[-]jcsp-j) - 第 3 个字符
'j':出现在x+x下标 4 处(csp-[j]csp-j) - 第 4 个字符
'c':出现在x+x下标 5 处(csp-j[c]sp-j) - 第 5 个字符
's':出现在x+x下标 6 处(csp-jc[s]p-j) 按顺序依次匹配下标 ,完美找到了子序列"p-jcs"。
- 第 1 个字符
- 计算结果:LCS 长度 ,
g函数返回true。 - 输出格式:在 C++ 标准库中,
cout默认将布尔值true打印为整数1(false打印为0)。因此输出为1。
6. 当输入为 csppsc spsccp 时,输出为( )。
A.
T
B.F
C.0
D.1
正确答案: D
深度解析:
- 参数分析:
"csppsc"(长度 6),"spsccp"(长度 6)。长度相等。 - 拼接字符串:
"csppsccsppsc"。 - 匹配子序列:在 中按顺序查找
"spsccp"的每个字符:- 第 1 个字符
's':取第 1 个字符(下标 1:c[s]ppsc...) - 第 2 个字符
'p':取第 2 个字符(下标 2:cs[p]psc...) - 第 3 个字符
's':取第 4 个字符(下标 4:cspp[s]c...) - 第 4 个字符
'c':取第 5 个字符(下标 5:cspps[c]...) - 第 5 个字符
'c':取第 6 个字符(下标 6:csppsc[c]...) - 第 6 个字符
'p':取第 8 个字符(下标 8:csppsccs[p]...) 匹配到的下标序列为 ,成功拼出"spsccp"。
- 第 1 个字符
- 计算结果:LCS 长度 ,
g函数返回true,cout打印出1。
📍 第三题:因子平方和计算与数论推演
💻 源码展示
#include <iostream>
#include <cmath>
using namespace std;
int solve1(int n){
return n*n;
}
int solve2(int n){
int sum=0;
for(int i=1;i<=sqrt(n);i++){
if(n%i==0){
if(n/i==i){
sum+=i*i;
}else{
sum+=i*i+(n/i)*(n/i);
}
}
}
return sum;
}
int main(){
int n;
cin>>n;
cout<<solve2(solve1(n))<<" "<<solve1(solve2(n))<<endl;
return 0;
}假设输入的 是绝对值不超过 1000 的整数,完成下面的判断题和单选题:
💡 程序主旨
solve1(n)函数:计算并返回 。solve2(n)函数:计算正整数 的 所有因子的平方和。- 使用试除法枚举 。
- 若 能整除 ,则 与 均为 的因子。
- 当 时(即 为完全平方数),只累加一次 ;否则累加 。
main函数:- 第一项输出:
solve2(solve1(n)),即计算 的所有因子的平方和; - 第二项输出:
solve1(solve2(n)),即计算 ( 的所有因子的平方和)的平方。
- 第一项输出:
❓ 判断题
1. 如果输入的 为正整数,solve2 函数的作用是计算 所有的因子的平方和( )
A. 正确
B. 错误
正确答案: A
深度解析:
试除法通过循环 ,找到 的每一对因子 。对于每一对因子,代码分别计算了它们的平方 和 并进行累加。这种方法完整且不重不漏地计算了正整数 的所有约数(因子)的平方之和。因此说法正确。
2. 第 13 ~ 14 行的作用是避免 的平方根因子 (或 )进入第 16 行而被计算两次( )
A. 正确
B. 错误
正确答案: A
深度解析:
当 是一个完全平方数时(例如 ,),满足 。
若不进行第 1314 行的特判直接执行第 16 行 14 行的 sum += i*i + (n/i)*(n/i),则 会被加两次(算成 )。13if(n/i == i) sum += i*i; 正是为了去重,防止平方根因子被计算两次。故本题说法正确。
3. 如果输入的 为质数,solve2(n) 的返回值为 ( )
A. 正确
B. 错误
正确答案: A
深度解析:
若 为质数(如 2, 3, 5, 7 …),根据质数的定义,其正因子有且仅有两个: 和 。
因此,solve2(n) 计算的因子平方和为:
说法完全正确。
❓ 单选题
4. (4分)如果输入的 为质数 的平方,那么 solve2(n) 的返回值为( )
A.
B.
C.
D.
正确答案: B
深度解析:
若输入 (其中 为质数):
- 的所有正因子为:(共 3 个因子)。
solve2(n)返回这 3 个因子的平方和:- 将 代入上式可得 ,故: 对应 B 选项。
5. 当输入为正整数时,第一项减去第二项的差值一定( )
A. 大于 0
B. 大于等于 0 且不一定大于 0
C. 小于 0
D. 小于等于 0 且不一定小于 0
正确答案: D
深度解析:
我们需要比较第一项 solve2(solve1(n)) 与第二项 solve1(solve2(n)) 的大小关系。
设第一项为 ,第二项为 。
我们通过代入具体数字校验:
- 当 时:
- ,,故 。
- ,,故 。
- 差值 。
- 当 时:
- 第一项 。 的因子为 ,其平方和 。
- 第二项 。 的因子为 ,其平方和为 ,故 。
- 差值 。
综上所述,差值可以等于 0(当 时),也可以小于 0(当 时)。因此差值 小于等于 0 且不一定小于 0,对应 D 选项。
6. 当输入为 5 时,输出为( )
A.
651 625
B.650 729
C.651 676
D.652 625
正确答案: C
深度解析:
当输入 时:
- 计算第一项
solve2(solve1(5)):solve1(5)。solve2(25): 的因子有 。- 第一项 。
- 计算第二项
solve1(solve2(5)):solve2(5): 是质数,因子有 ,其平方和 。solve1(26)。
- 拼装输出:程序输出用空格分隔的两项,结果为
651 676。对应 C 选项。
💡 总结与解题技巧
在 CSP-J 阅读程序题中,拿到高分的核心在于:
- 宏观理解程序目的:切忌一上来就机械盲目代入数据计算。先看包含哪些函数,根据关键公式(如海伦公式、LCS 动态规划、试除法)判断程序功能,能大幅提升答题效率。
- 注意语言与输出细节:如 C++ 浮点输出格式
fixed+precision、bool变量用cout输出的默认格式(1或0)、vector/数组下标越界对程序的影响等。 - 擅用特殊值与特例代入:在做性质判断与公式单选题(如第 3 大题第 5 小题)时,构造 等小样本数据快速验证,是避开复杂证明、高准确率解题的捷径!
本文由coderli.com原创,按照CC BY-NC-SA 4.0 进行授权
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
“luogu-”系列题目可在 洛谷题库 在线评测。
“bcqm-”系列题目可在 编程启蒙题库 在线评测。
GESP/CSP认证交流QQ群: 688906745
