历年CSP-J初赛真题解析 | 2019年CSP-J初赛
欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
- 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
- 希望系统学习C++/Python编程的初学者
- 想要提升算法与编程能力的编程爱好者
附上汇总贴:历年CSP-J初赛真题解析 | 汇总_热爱编程的通信人的博客-CSDN博客
单项选择
第1题
中国的国家顶级域名是( )
A..cn
B..ch
C..chn
D..china
【答案】:A
【解析】
之前的原题
第2题
二进制数11 1011 1001 0111和01 0110 1110 1011进行逻辑与运算的结果是( )。
A.01 0010 1000 1011
B.01 0010 1001 0011
C.01 0010 1000 0001
D.01 0010 1000 0011
【答案】:D
【解析】
逻辑与、或运算
第3题
一个32位整型变量占用( )个字节。
A.32
B.128
C.4
D.8
【答案】:C
【解析】
1个字节8位,32位4个字节
第4题
若有如下程序段,其中s、a、b、c均已定义为整型变量,且a、c均已赋值(c大于0)
s=a;
for (b=1; b<=c; b++) s = s - 1;
则与上述程序段功能等价的赋值语句是( )
A.s=a-c;
B.s=a-b;
C.s=s-c;
D.s=b-c;
【答案】:A
【解析】
同2014年,循环c次表示减c
第5题
设有100个已排好序的数据元素,采用折半查找时,最大比较次数为( )
A.7
B.10
C.6
D.8
【答案】:A
【解析】
同2015年,折半查找,无论是否排好序,都需要7次
第6题
链表不具有的特点是( )
A.插入删除不需要移动元素
B.不必事先估计存储空间
C.所需空间与线性表长度成正比
D.可随机访问任一元素
【答案】:D
【解析】
同之前(差不多考了4次)
第7题
把8个同样的球放在5个同样的袋子里,允许有的袋子空着不放,问共有多少种不同的分法?( )
提示:如果8个球都放在一个袋子里,无论是哪个袋子,都只算同一种分法。
A.22
B.24
C.18
D.20
【答案】:C
【解析】
8个同样的球放入1个同样的袋子:1种(80000)
8个同样的球放入2个同样的袋子:4种(71000、62000、53000、44000)
8个同样的球放入3个同样的袋子:5种(61100、52100、43100、42200、33200)
8个同样的球放入4个同样的袋子:5种(51110、42110、33110、32210、22220)
8个同样的球放入5个同样的袋子:3种(41111、32111、22211)
第8题
一棵二叉树如右图所示,若采用顺序存储结构,即用一维数组元素存储该二叉树中的结点(根结点的下标为1,若某结点的下标为i,则其左孩子位于下标2i 处、右孩子位于下标2i+1处),则该数组的最大下标至少为( )。

A.6
B.10
C.15
D.12
【答案】:C
【解析】
之前的原题
第9题
100以内最大的素数是( )。
A.89
B.97
C.91
D.93
【答案】:B
【解析】
从100往下枚举
第10题
319和377的最大公约数是( )。
A.27
B.33
C.29
D.31
【答案】:C
【解析】
辗转相除法,gcd(b, a%b),计算得到29
第11题
新学期开学了,小胖想减肥,健身教练给小胖制定了两个训练方案。
方案一:每次连续跑3公里可以消耗300千卡(耗时半小时);
方案二:每次连续跑5公里可以消耗600千卡(耗时1小时)。
小胖每周周一到周四能抽出半小时跑步,周五到周日能抽出一小时跑步。另外,教练建议小胖每周最多跑21公里,否则会损伤膝盖
请问如果小胖想严格执行教练的训练方案,并且不想损伤膝盖,每周最多通过跑步消耗多少千卡?( )
A.3000
B.2500
C.2400
D.2520
【答案】:C
【解析】
设方案一跑x天,方案二跑y天
3x+5y≤21 => x≤(21-5y)/3
x+y≤7 => x≤7-y
y≤3 => y≤3
| y=3 | y=2 | y=1 | y=0 | |
|---|---|---|---|---|
| x | x≤2,x≤4,取x=2 | x≤11/3,x≤5,取x=3 | x≤16/3,x≤6,取x=5 | x≤7,x≤7,取x=7 |
| 最多 | 300x+600y=2400 | 300x+600y=2100 | 300x+600y=2100 |
300x+600y=2100 |
第12题
一副纸牌除掉大小王有52张牌,四种花色,每种花色13张。假设从这52张牌中随机抽取13张纸牌,则至少( )张牌的花色一致。
A.4
B.2
C.3
D.5
【答案】:A
【解析】
抽屉原理,每个花色放3张,共12张,第13张随便选择哪个花色,则至少4张牌的花色一致
第13题
一些数字可以颠倒过来看,例如0、1、8颠倒过来还是本身,6颠倒过来是9,9颠倒过来看还是6,其他数字颠倒过来都不构成数字。
类似的,一些多位数也可以颠倒过来看,比如106颠倒过来是901。假设某个城市的车牌只由5位数字组成,每一位都可以取0到9。
请问这个城市最多有多少个车牌倒过来恰好还是原来的车牌?( )
A.60
B.125
C.75
D.100
【答案】:C
【解析】
\(a_3\)可以填8或0或1,\(a_1\)可以填8或0或1或6或9,同理\(a_2\),\(a_4\)和\(a_5\)由\(a_1\)、\(a_2\)决定
\(5*5*3=75\)
第14题
假设一棵二叉树的后序遍历序列为DGJHEBIFCA, 中序遍历序列为DBGEHJACIF, 则其前序遍历序列为( )。
A.ABCDEFGHIJ
B.ABDEGHJCFI
C.ABDEGJHCFI
D.ABDEGHJFIC
【答案】:B
【解析】
根据后序遍历和中序遍历画出树,再进行先序遍历
第15题
以下哪个奖项是计算机科学领域的最高奖?( )
A.图灵奖
B.鲁班奖
C.诺贝尔奖
D.普利策奖
【答案】:A
【解析】
(考了5次了!)
阅读程序
#include <cstdio>
#include <cstring>
using namespace std;
char st[100];
int main() {
scanf("%s", st);
int n = strlen(st);
for (int i=1; i<=n; ++i) {
if (n % i == 0) {
char c = st[i-1];
if (c >= 'a')
st[i-1] = c - 'a' + 'A';
}
}
printf("%s", st);
return 0;
}
第16题
输入的字符只能由小写字母或大写字母组成。( )
A.对
B.错
【答案】:B
【解析】
输入c为空格,第11行的if语句不发生,输出结果也没有影响。
第17题
若将第8行的"i=1"改为"i=0",程序运行时会发生错误。( )
A.对
B.错
【答案】:A
【解析】
i=0,n%0,程序会报错
第18题
若将第8行的"i<=n"改为"i*i<=n",程序运行结果不会改变。( )
A.对
B.错
【答案】:B
【解析】
使用代入法计算,超过\(\sqrt n\)的数,虽然也是n的约数,但语句却未执行
第19题
若输入的字符串全部由大写字母组成,那么输出的字符串就跟输入的字符串一样。( )
A.对
B.错
【答案】:A
【解析】
输入字符串都是大写字母,第11行语句不会执行,输出也都是大写字母
第20题
若输入的字符串长度为18,那么输入的字符串跟输出的字符串相比,至多有( )个字符不同。
A.18
B.6
C.10
D.1
【答案】:B
【解析】
18的约数个数是6个,分别为1,2,4,8,9,16
第21题
若输入的字符串长度为( ),那么输入的字符串跟输出的字符串相比,至多有36个字符不同。
A.36
B.100000
C.1
D.128
【答案】:B
【解析】
就是求那个长度有36个约数。
\(10000=2^5 \times 5^5\),(5+1) * (5+1)=36,就是10000的约数个数
#include <cstdio>
using namespace std;
int n,m;
int a[100], b[100];
int main() {
scanf("%d%d", &n, &m);
for (int i=1; i<=n; ++i)
a[i] = b[i] = 0;
for (int i=1; i<=m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
if (a[x]<y && b[y]<x) {
if (a[x]>0)
b[a[x]] = 0;
if (b[y]>0)
a[b[y]] = 0;
a[x] = y;
b[y] = x;
}
}
int ans = 0;
for (int i=1; i<=n; ++i) {
if (a[i]==0)
++ans;
if (b[i]==0)
++ans;
}
printf("%d\n", ans);
return 0;
}
假设输入的n和m都是正整数,x和y都是在[1,n]范围内的整数,完成下面的判断题和单选题:
第22题
当m>0时,输出值一定小于2n。( )
A.对
B.错
【答案】:A
【解析】
假设n=5,m=5
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| a | 0 | 0 | 0 | 0 | 0 |
| b | 0 | 0 | 0 | 0 | 0 |
| x=2,y=4后a,b |
0 | 4 | 0 | 0 | 0 |
| 0 | 0 | 0 | 2 | 0 | |
| x=4,y=2后a,b |
0 | 4 | 0 | 2 | 0 |
| 0 | 4 | 0 | 2 | 0 | |
| x=3,y=5后a,b |
0 | 4 | 5 | 2 | 0 |
| 0 | 4 | 0 | 2 | 3 | |
| x=4,y=4后a,b (原先x=3) |
0 | 0 | 5 | 4 | 0 |
| 0 | 0 | 0 | 4 | 3 | |
| x=4,y=5后a,b |
0 | 0 | 0 | 5 | 0 |
| 0 | 0 | 0 | 0 | 4 |
最后计算ans=8。
题目是求当x出现多次时,选择对应y的最大值,以及y出现多次时,选择对应x的最大值。如果不再选择,则将其改为0。如x=4,y=2。
执行第13行后,第一对x和y,就会执行第18和第19行,就一定有a[i]或b[i]不等于0,则ans一定小于2n。
第23题
执行完第27行的"++ans"时, ans一定是偶数。( )
A.对
B.错
【答案】:B
【解析】
n=2,m=1,x=1,y=2,i=1时,ans就是1。
第24题
a[i]和b[i]不可能同时大于0。( )
A.对
B.错
【答案】:B
【解析】
刚才举例中,x=4,y=4,执行后a[i]和b[i]同时大于0
第25题
若程序执行到第13行时,x总是小于y,那么第15行不会被执行。( )
A.对
B.错
【答案】:B
【解析】
程序中都是x与x比较,y与y比较,与x和y的大小关系无关,按之前的举例,第15行就被执行了。
第26题
若m个x两两不同,且m个y两两不同,则输出的值为( )。
A.2n-2m
B.2n+2
C.2n-2
D.2n
【答案】:A
【解析】
如果都是两两不同,每次都是2m个0小时,所以最后ans=2n-2m
第27题
若m个x两两不同,且m个y都相等,则输出的值为( )。
A.2n-2
B.2n
C.2m
D.2n-2m
【答案】:A
【解析】
多个相同的y,会选择最大的x,那么最后只有y和选中的那个x,a[y]和a[x]这两个不为0,所以ans=2n-2
#include <iostream>
using namespace std;
const int maxn = 10000;
int n;
int a[maxn], b[maxn];
int f(int l, int r, int depth) {
if (l > r)
return 0;
int min = maxn, mink;
for (int i=l; i<=r; ++i) {
if (min>a[i]) {
min = a[i];
mink = i;
}
}
int lres = f(l, mink-1, depth+1);
int rres = f(mink+1, r, depth+1);
return lres + rres + depth * b[mink];
}
int main() {
cin >> n;
for (int i=0; i<n; ++i)
cin >> a[i];
for (int i=0; i<n; ++i)
cin >> b[i];
cout << f(0, n-1, 1) << endl;
return 0;
}
第28题
如果a数组有重复的数字,则程序运行会发生错误。( )
A.对
B.错
【答案】:B
【解析】
没有存在运行错误的代码,常见的如数组越界、n作为除数、sqrt(-1)等
第29题
如果b数组全为0,则输出为0。( )
A.对
B.错
【答案】:A
【解析】
最后是depth * b[mink],如果b数组为0,则计算结果一定为0
第30题
当n=100时,最坏情况下,与第12行的比较运算执行的次数最接近的是:( )。
A.5000
B.600
C.6
D.100
【答案】:A
【解析】
二叉树,最深是链形,则最深是100层。100+99+98+...+1=5050
第31题
当n=100时,最好情况下,与第12行的比较运算执行的次数最接近的是:( )。
A.100
B.6
C.5000
D.600
【答案】:D
【解析】
最好情况是6层深度的二叉树,每层比较次数约100次。故为600次
第32题
当n=10时,若b数组满足,对任意0≤i<n,都有b[i]=i+1,那么输出最大为( )。
A.386
B.383
C.384
D.385
【答案】:D
【解析】
最大为1 * 1 + 2 * 2 + 3 * 3 + ... + 9 * 9 + 10 * 10 = 385
第33题
当n=100时,若b数组满足,对任意0≤i<n,都有b[i]=1,那么输出最小为( )。
A.582
B.580
C.579
D.581
【答案】:B
【解析】
深度越浅越好,1 * 1 + 2 * 2 + 3 * 4 + 4 * 8 + 5 * 16 + 6 * 32 + 7 * 37 = 580
完善程序
矩阵变幻
有一个奇幻的矩阵,在不停的变幻,其变幻方式为:数字0变成矩阵\(\begin{bmatrix} 0&0\\ 0&1\end{bmatrix}\),数字1变成矩阵\(\begin{bmatrix} 1&1\\ 1&0\end{bmatrix}\)
最初矩阵只有一个元素0,变幻n次后,矩阵会变成什么样?
例如,矩阵最初为[0];变幻1次后:\(\begin{bmatrix} 0&0\\ 0&1\end{bmatrix}\);变幻2次后:\(\begin{bmatrix} 0&0&0&0\\ 0&1&0&1\\ 0&0&1&1\\ 0&1&1&0\end{bmatrix}\)
输入一行一个不超过10个正整数n,输出变幻n次后的矩阵。试补全程序。
提示:"<<"表示二进制左移运算符,例如\((11)_2<<2 = (1100)_2\)
#include <cstdio>
using namespace std;
int n;
const int max_size = 1 << 10;
int res[max_size][max_size];
void recursive(int x, int y, int n, int t) {
if (n==0) {
res[x][y] = __1__;
return;
}
int step = 1 << (n-1);
recursive(__2__, n-1, t);
recursive(x, y+step, n-1, t);
recursive(x+step, y, n-1, t);
recursive(__3__; n-1, !t);
}
int main()
{
scanf("%d", &n);
recursive(0, 0, __4__);
int size = __5__;
for (int i=0; i<size; ++i) {
for (int j=0; j<size; ++j)
printf("%d", res[i][j]);
put("");
}
return 0;
}
第34题
1处应该填( )。
A.n%2
B.0
C.t
D.1
【答案】:C
【解析】
填入res[x][y]的值,不可能全为0或全为1,只能为t
第35题
2处应该填( )。
A.x-step, y-step
B.x, y-step
C.x-step, y
D.x, y
【答案】:D
【解析】
左上角就是原来的坐标
第36题
3处应该填( )。
A.x-step, y-step
B.x+step, y+step
C.x-step, y
D.x, y-step
【答案】:B
【解析】
右下角左边,所以x+step,y+step
第37题
4处应该填( )。
A.n-1, n%2
B.n, 0
C.n, n%2
D.n-1, 0
【答案】:B
【解析】
可以带入n,0和n,1,观察结果发现n,0为正确答案
第38题
5处应该填( )。
A.1<<(n+1)
B.1<<n
C.n+1
D.1<<(n-1)
【答案】:B
【解析】
1的边长是2,2的边长是4,n的边长是\(2^n\),即1<<n
计数排序
计数排序是一个广泛使用的排序方法。下面的程序使用双关键字计数排序,将n对10000以内的整数,从小到大排序。
例如有三对整数(3,4)、(2,4)、(3,3),那么排序之后应该是(2,4)、(3,3)、(3,4)。
输入第一行为n,接下来n行,第i行有两个数a[i]和b[],分别表示第i对正整数的第一关键字和第二关键字。
从小到大排序后输出。数据范围\(1\le n\le 10^7, 1\le a[i],b[i]\le 10^4\)
提示:应先对第二关键字排序, 再对第一关键字排序。数组ord[]存储第二关键字排序的结果, 数组res[]存储双关键字排序的结果。
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10000000;
const int maxs = 10000;
int n;
unsigned a[maxn], b[maxn], res[maxn], ord[maxn];
unsigned cnt[maxs+1];
int main()
{
scanf("%d", &n);
for (int i=0; i<n; ++i)
scanf("%d%d", &a[i], &b[i]);
memset(cnt, 0, sizeof(cnt));
for (int i=0; i<n; ++i)
__1__; // 利用cnt数组统计数量
for (int i=0; i<maxs; ++i)
cnt[i+1] += cnt[i];
for (int i=0; i<n; ++i)
__2__; // 记录初步排序结果
memset(cnt, 0, sizeof(cnt));
for (int i=0; i<n; ++i)
__3__; // 利用cnt数组统计数量
for (int i=0; i<maxs; ++i)
cnt[i+1] += cnt[i];
for (int i=n-1; i>=0; --i)
__4__; // 记录最终排序结果
for (int i=0; i<n; ++i)
printf("%d %d\n", __5__);
return 0;
}
第39题
1处应该填( )。
A.++cnt[i]
B.++cnt[b[i]]
C.++cnt[a[i] * max + b[i]]
D.++cnt[a[i]]
【答案】:B
【解析】
因为要先对第二关键字(b[i])排序,所以选B
第40题
2处应该填( )。
A.ord[--cnt[a[i]]] = i
B.ord[--cnt[b[i]]] = a[i]
C.ord[--cnt[a[i]]] = b[i]
D.ord[--cnt[b[i]]] = i
【答案】:D
【解析】
第20行是为了计算cnt[b[i]]的前缀和,即每个b[i]数字的排名,所以A、C错误
ord[x]记录第x名所对应的下标
第41题
3处应该填( )。
A.++cnt[b[i]]
B.++cnt[a[i] * maxs + b[i]]
C.++cnt[a[i]]
D.++cnt[i]
【答案】:C
【解析】
统计第1关键字,所以选C
第42题
4处应该填( )。
A.res[--cnt[a[ord[i]]]] = ord[i]
B.res[--cnt[b[ord[i]]]] = ord[i]
C.res[--cnt[b[i]]] = ord[i]
D.res[--cnt[a[i]]] = ord[i]
【答案】:A
【解析】
这题的排序思路:十位数相同的个位数大的先放,在对应的十位数区间从后向前放
第28行之前ord[]={21,31,11,32,122,22}
倒序查找ord[5]为22,a[22]为2,cnt[2]为4,先--cnt[2]得3,然后res[3]=ord[5]=22,即把22放到res数组中下标为3的位置。
依此类推,最后res[]={11,12,21,22,31,32}
第43题
5处应该填( )。
A.a[i], b[i]
B.a[res[i]], b[res[i]]
C.a[ord[res[i]]], b[ord[res[i]]]
D.a[res[ord[i]]], b[res[ord[i]]]
【答案】:B
【解析】
res[i]记录第i个数的在原序列的位置。且a、b数组内容必然成对输出,下标必须一致,也可以简单猜测选B

浙公网安备 33010602011771号