CSP 2020 第一轮(初赛)模拟解析

一、十进制数 114 的相反数的 8 位二进制补码是:

A. 10001110       B. 10001101      C. 01110010       D. 01110011

点击查看答案
根据原码补码反码的有关定义可得:
-114 的源码为 :0111 0010
       反码为 :1000 1101
     故补码为 :1001 1110  
     
下贴原码补码反码的相关定义:
    正数的原码、反码、补码均相同。
    原码:用最高位表示符号位,其余位表示数值位的编码称为原码。其中,正数的符号位为 0,负数的符号位为 1
    负数的反码:把原码的符号位保持不变,数值位逐位取反,即可得原码的反码。
    负数的补码:在反码的基础上加 1 即得该原码的补码。
    
此题为简单题,熟记概念即可,不应扣分。

二、以下哪个网站不是 Online Judge(在线程序判题系统)?Online Judge 可以查看算法题目,提交自己编写的程序,然后可以获得评测机反馈的结果。

A. Luogu       B. Gitee       C. LeetCode       D. Codeforces

点击查看答案
A.Luogu 洛谷是基于网页形式的信息学在线评测系统。同时具有多种社区功能
B.Gitee 又称码云,是基于Git的代码托管网站,约等于Github
C.LeetCode  又名力扣,是一个为全球程序员提供 IT 技术职业化提升的平台,提供了完善的在线判题服务(OJ)、学习工具、社区讨论及模拟面试功能
D.Codeforces 是一家为计算机编程爱好者提供在线评测系统的俄罗斯网站。

以上部分内容来自于百度

此题为简单题,不应扣分。

三、小 A 用字母 A 表示 1B 表示 2,以此类推,用 Z 表示26 。对于 27 以上的数字,可以用两位或者更长的字符串来对应,例如 AA 对应 27AB 对应 28AZ 对应 52AAA 对应 703 那么 BYT 字符串对应的数字是什么?

A. 2018       B.2020       C.2022       D.2024

点击查看答案
本题可近似理解为进制转换:
故的以下等式:
BYT = 2*26*26 + 25*26 + 20 = 2022

此题为简单题,核心在理解题面,不应扣分。

四、UIM 拍摄了一张照片,其分辨率是 4096×2160,每一个像素都是 24 位真彩色。在没有压缩的情况下,这张图片占用空间接近以下哪个值?
A.8MB       B.25MB       C.200MB       D.200KB

点击查看答案
带入图片内存大小运算公式:
图片所占内存大小 = 图片长度(像素) * 图片宽度(像素) * 一个像素所占内存空间
                = 4096 * 2160 * 24 = 212336640 bit = 26542080Byte
                = 25920 KB = 25.3125 MB

此题为简单题,理解核心公式,不应扣分。

五、在一个长度为 n 的数组中找到第 k 大的数字,平均的算法时间复杂度最低的是:

A.O(n)       B.O(nk)       C.O(nlogn)       D.O(n2)

点击查看答案
通过简单的朴素思路可以将BCD三个选项的算法设计
B 通过冒泡排序或者选择排序的排序规则,第k大值会在第k轮被排入有序序列,进行k轮冒泡或选择排序即可得到结果。
C 这个复杂度的算法非常多样,包括但不限于快速排序直接查找,构建平衡树
D 暴力枚举比大小,亦或插入排序
这时考虑如何设计A项复杂度的算法:(相对有点难度,不过是经典的模型)
1. 首先任选一个树x,将原数组A分为Sa,Sb两个部分,满足Sa中的元素均大于等于x,Sb中元素均小于等于x
      若Sa中的元素个数小于k,那么Sb中的第k-|Sa|大的元素即为答案
      反之,Sa中第k大的元素的答案
2.按照规则分治递归处理

故平均复杂度可以用递推表达式表示为 T(n) = 2T(n/2) + O(1),即复杂度为 O(n)

此题难度较难,可以允许扣分

六、对于“树”这种数据结构,正确的有:
①一个有 n 个顶点、n1 条边的图是树
②一个树中的两个顶点之间有且只有一条简单路径
③树中一定存在度数不大于 1 的顶点
④树可能存在环

A. ①②④       B. ①②③       C. ②③       D. ①②

点击查看答案
①树保证连通性
④根据树的定义树上不存在环

此题为简单题,熟记概念即可,不应扣分。

七、博艾中学进行了一次信息学会考测试,其优、良、及格、不及格的试卷数量分别为 10,13,14,5张。现在这些卷子混在一起,要将这些卷子按照等级分为 4 叠。分卷子的方法是,每次将一叠有不同等级答卷的卷子分为两堆,使得这两堆中没有相同等级的卷子,然后可以再分,直到分为 4 叠。要分完这些卷子,至少需要多少次“分卷子”的操作?将一堆数量为 n 的卷子分成两堆,就会产生 n 次分卷子的操作。

A. 84       B. 93       C. 78      D. 85

点击查看答案
由题目题面的“将一堆数量为 n 的卷子分成两堆,就会产生 n 次分卷子的操作”。
那么可以得到很直观的贪心思维,应该让每一轮产生还继续分的堆的卷子尽量少:
于是得到两个算法,相对平均分成两堆在分开 : 10+13+14+5+13+10+14+5 = 84
                从大到小挑出卷子: 10+13+14+5+10+13+5+10+5+5=85
故得到答案

此题为简单题,争取不扣分。

八、一个二叉树的前序遍历是 HGBDAFEC,中序遍历是 BGHFAEDC,同时采用顺序存储结构,即用一维数组元素存储该二叉树中的结点(根结点的下标为 1,若某结点的下标为 i,则其左孩子位于下标 2i 处、右孩子位于下标 2i+1 处),则该数组的最大下标至少为( )

A. 7       B. 13       C. 15       D. 12

点击查看答案
1.先序遍历
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树。
2.中序遍历
(1)中序遍历左子树;
(2)访问根结点;
(3)中序遍历右子树。
3.后序遍历
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点。

第一步:还是先求root根节点,根据前序遍历规则我们可知root为前序遍历序列的第一个节点,因此该二叉树的root节点为H。
第二步:求root的左子树和右子树,这点我们还是从中序遍历序列中找出,位于root节点H左侧的B-G为root的左子树,位于H右侧的F-A-E-D-C为右子树。
第三步:求root的左子树的根节点和右子树的根节点。我们可以找到左子树B-G在前遍历序列中的排列顺序为G-B,由于前序遍历最先访问根节点,所以G为左子树的根节点,右子树同理
第四步:我们可以根据上面的步骤找到G的左子树和右子树,以及D的左子树和右子树,然后分别求出左右子树的根节点。以此类推,只要求出根节点及其和,剩下的过程都是递归的,最后我们就可以还原整个二叉树。

一直后序和中序求前序同理

此题为简单题,不应扣分。

九、在 C++ 语言中,如果 a=1 ;b=0 ;c=1 ; 那么以下表达式中为 1 的是:

A. a&&b||b&&c       B. a+b>c||b       C. !(!c&&(!a||b))       D. a+b+c

点击查看答案
A a&&b||b&&c = 0 || 0 = 0
B a+b>c||b = 1 + 0 > 1 || 0 = 0 || 0 = 0
C !(!c&&(!a||b)) = !(!c&&(0||0)) = !(0&&0) = !0 = 1
D a+b+c = 2

十、在一个初始长度为 n 的链表中连续进行 k 次操作,每次操作是读入两个数字 aibi,在链表中找到元素为 ai 的结点(假设一定可以找到),然后将 bi 这个元素插入到这个结点前面。在最理想的情况下,链表访问的结点数量最少可能是多少(不算将要插入的结点)?

A. n      B. k      C. nk      D. n+k

点击查看答案
每次均插在第二个,即只需要遍历 n 次

此题为简单题,牢记运算优先级,不应扣分。

十一、A 班有 5 名风纪委员,B 班有 4 名风纪委员,C 班有 3 名风纪委员。现在需要这些同学中选取 6 名风纪委员巡逻,如果只关注各班派出的风纪委员人数,有几种不同的方案?

A. 9       B. 12       C. 15       D. 18

点击查看答案
暴力分类讨论计算:以C班选几个人为分类依据得:答案为4+5+5+4 = 18

此题为简单题,牢记运算优先级,不应扣分。

十二、以下哪种排序算法的时间复杂度是 O(n2)

A. 计数排序       B. 插入排序       C. 希尔排序       D. 归并排序

点击查看答案
此题为简单题,不应扣分。

十三、已知 rand() 可以生成一个 032767 的随机整数,如果希望得到一个范围在 [a,b) 的随机整数,ab 均是不超过 100 的正整数且 a<b,那么可行的表达式是什么?

A. (rand()%(b-a))+a       B. (rand()%(b-a+1))+a       C. (rand()%(b-a))+a+1       D. (rand()%(b-a+1))+a+1

点击查看答案
根据rand函数的定义得:
A:[a,b)
B:[a,b]
C:(a,b]
D:(a,b+1]

此题为简单题,不应扣分。

十四、一个 7 个顶点的完全图需要至少删掉多少条边才能变为森林?

A. 16       B. 21       C. 15       D. 6

点击查看答案
森林的定义:每个连通分量都是树的图
由定义可得:一棵树也是一个森林
故一个n个点的森林最多只有n-1条边
而n个点的完全图有n * (n-1) / 2
故答案为 21 - 6 = 15


此题为简单题,不应扣分。

十五、2020 年 8 月,第( )届全国青少年信息学奥林匹克竞赛在( )举行?

A. 26,广州       B. 26,长沙       C. 37,广州       D. 37,长沙

点击查看答案
逆天背诵题,没实践意义。

十六、

#include<iostream>
using namespace std;
#define MAXN 20
int gu[MAXN][MAXN];
int luo(int n, int m) {
    if(n <= 1 || m < 2)
        return 1;
    if(gu[n][m] != -1)
        return gu[n][m];
    int ans = 0;
    for(int i = 0; i < m; i += 2)
        ans += luo(n - 1, i);
    gu[n][m] = ans;
    return ans;
}
int main() {
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < MAXN; i++)
        for(int j = 0; j < MAXN; j++)
            gu[i][j] = -1;
    cout << luo(n, m);
    return 0;
} 

判断题

1.(1 分)luo 函数中,m 的值不可能是奇数。( )

A. 正确       B. 错误

2.(1 分)若将第 11 行的 < 改为 <=,程序的输出结果可能会改变。( )

A. 正确       B. 错误

3.若将第 8,9,13 行删除,程序的运行的结果不变( )

A. 正确       B. 错误

4.在添加合适的头文件后,将第 19 到 21 行替换为 memset(gu,255,sizeof(gu)); 可以起到相同的作用( )

A. 正确       B. 错误

选择题

5.(4 分)若输入数据为 4 8,则输出为( )。

A. 7       B. 8       C. 15       D. 16

6.最坏情况下,此程序的时间复杂度是( )。

A. O(m2n)       B. O(nm!)       C. O(n2)       D. O(n2m)

点击查看答案
1.通过简易的举反例即可得到结果: n=1,m=1可以运行
核心是m奇偶性对luo函数的执行没有影响,执行影响运算结果。

2.若m为偶数,那么如果更改ans运算过程中会多加一个值luo(n-1,m)

3.根据代码的设计,该函数显然在进行记忆化搜索,其中第8、9、13行就起到记忆化的作用
若删去则luo函数变为普通的深度优先搜索,但这样的更改并不会影响运算结果只会影响运算效率

4.本体重点考察memset函数的底层运算逻辑。memset(void *s, int ch, size_t n);函数将s中当前位置后面的n个字节用 ch 替换并返回 s。
根据函数的功能可以看到memset是以字节为基本单位进行操作的。而int类型有4个字节大大小,且255用二进制表示为1111 1111
故如调用memset(gu,255,sizeof(gu))则gu函数中的变量会被赋为 1111 1111 1111 1111 1111 1111 1111 1111 即-1

5.列表,将记忆化搜索转为dp进行计算,如下表:

6.因为luo函数进行了记忆化操作实际上复杂度就变为2维的dp,由转移方程得单次转移需要遍历O(m)级别的点
故算法整体复杂度为A
n\m 0 1 2 3 4 5 6 7 8
0 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
2 1 1 1 2 2 3 3 4 4
3 1 1 1 2 2 4 4 7 7
4 1 1 1 2 2 4 4 8 8

十七、

#include<bits/stdc++.h>
using namespace std;
int n, m;
int f[101][101];
int F[101][101];
int main() {
  scanf("%d%d", &n, &m); // n的值在1到100之间
  memset(f, -1, sizeof(f));
  for(int i = 1; i <= m; i++) {
    int u, v, w; // w的值在0到10000之间
    scanf("%d%d%d", &u, &v, &w);
    f[u][v] = f[v][u] = w;
  }
  for(int k = 1; k <= n; k++)
    for(int i = 1; i <= n; i++)
      for(int j = 1; j <= n; j++)
        if(f[i][k] != -1 && f[k][j] != -1)
          if(f[i][j] == -1||f[i][j]>f[k][j]+f[i][k])
            f[i][j] = f[i][k] + f[k][j];
  int ans = 2147483647;
  for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++) {
      for(int x = 1; x <= n; x++)
        for(int y = 1; y <= n; y++)
          F[x][y] = f[x][y];
      F[i][j] = F[j][i] = 0;
      for(int x = 1; x <= n; x++)
        for(int y = 1; y <= n; y++)
          if(F[x][y]==-1||F[x][y]>F[x][i]+F[i][y])
            F[x][y] = F[x][i] + F[i][y];
      for(int x = 1; x <= n; x++)
        for(int y = 1; y <= n; y++)
          if(F[x][y]==-1||F[x][y]>F[x][j]+F[j][y])
            F[x][y] = F[x][j] + F[j][y];
      int res = 0;
      for(int x = 1; x <= n; x++)
        for(int y = 1; y < x; y++)
          res += F[x][y];
      ans = min(res, ans);
    }
  printf("%d\n", ans);
  return 0;
} 

判断题
1.(1 分)14 到 16 行,将外层到内层的循环变量依次调整为 i,j,k,程序的运行的结果不变。( )

A. 正确       B. 错误

2.这个程序的时间复杂度和 m 无关。( )

A. 正确       B. 错误

3.20 行的 ans 如果初始化为 107 时,可能无法得到正确结果。( )

A. 正确       B. 错误

4.若将第 27 到 30 行的部分和 31 到 34 行的两个部分互换,程序的运行的结果不变。( )

A. 正确       B. 错误

选择题

5.若输入数据为 4 5/1 2 3/1 3 6/2 3 4/2 4 7/3 4 2(其中“/”为换行符),则输出为( )。

A. 14       B. 18       C. 21       D. 28

6.这个程序使用了( )算法。

A. Floyd       B. Dijkstra       C. Prim       D. Kruskal

点击查看答案
1.若将Floyd的内外层变量调换则可能会造成Floyd所求做短路可能错误

2.算法的复杂度表达为O(n^4+m),此时算法复杂的和m与n^4的级数有关

3.假设是链式的形态,只计算中间节点,节点长度ans就有最多 : 50 * 49 * 10000  >  1e7

4.i,j 从1到n的所有组合都被分类到故无需考虑i,j 的具体顺序。

5.根据Floyd处理将最短路可得下表
n\m 1 2 3 4
1 -1 3 6 8
2 3 -1 4 6
3 6 4 -2 2
4 8 6 2 -1
  本代码的意思的;添加一条从i到j的花费为0的路径再求最短路之和:
  那么若将1-4的边改为0.最短路和就变为了 3+2+0+2++4 = 14
6.很显然存在Floyd的代码段

十八、

#include<bits/stdc++.h>
using namespace std;
#define MOD 19260817
#define MAXN 1005
long long A[MAXN][MAXN] = {0}, sum[MAXN][MAXN] = {0};
int n, m, q;
int main() {
    A[1][1] = A[1][0] = 1;
    for(int i = 2; i <= 1000; i++) {
        A[i][0] = 1;
        for(int j = 1; j <= i; j++)
            A[i][j] = (A[i - 1][j] + A[i - 1][j - 1]) % MOD;
    }
    for(int i = 1; i <= 1000; i++)
        for(int j = 1; j <= 1000; j++)
            sum[i][j] = (sum[i - 1][j] + sum[i][j - 1] 
              - sum[i - 1][j - 1] + A[i][j] + MOD) % MOD;
    int q;
    cin >> q;
    while(q--) {
        int n, m;
        cin >> n >> m;
        cout << sum[n][m] << endl;
    }
    return 0;
} 

判断题
1.(1 分)当 ij 时,A[i][j] 的值是 0。( )

A. 正确       B. 错误

2.当 i>j 时,A[i][j] 的值相当于从 i 个不同元素中取出 j; 个元素的排列数。( )

A. 正确       B. 错误

3.sum[i][j] 的值(1<j<i1000)不小于 sum[i-1][j-1] 的值。( )

A. 正确       B. 错误

4.若将第 12 行改为 A[i][j]=(A[i-1][j]+A[i-1][j-1]+MOD)%MOD;,程序的运行的结果不变。( )

A. 正确       B. 错误

选择题
5.(4 分)A[i][j](1i10,1j10)的所有元素中,最大值是( )。

A. 126       B. 276       C. 252       D. 210

6.若输入数据为 1/5 3(其中“/”为换行符),则输出为( )。

A. 10       B. 35       C. 50       D. 24

点击查看答案
1.根据A的经典转移方程可以的到A[i][j]就是从i个元素中选j个的组合数,根据组合数的定义可以得到若i<j则A[i][j] = 0,若i=j则A[i][j] = 1

2.跟据上述的分析,A[i][j]是从i个元素中选j个的组合数

3.等存在模数的时候就可能以为取模的导致实际数值sum[i][j] < sum[i][j]

4.MOD = 19260817, 故3*MOD = 57782451 < INT_MAX,不会越界

5.因为 i,j 数值较小不涉及取模, 按照组合数的性质定理那么A[10][5]最大,最大值为252

6.求二维前缀和sum数组如下:
n\m 1 2 3 4 5
1 1 1 1 1 1
2 3 4 4 4 4
3 6 10 11 11 11
4 10 20 25 26 26
5 15 35 50 56 57

十九、

(封禁 xxs)现有 n 个 xxs(编号为 1n),每个 xxs 都有一个关注者,第 i 个 xxs 的关注者是 ai。现在管理员要将其中的一些 xxs 的账号封禁,但需要注意的是如果封禁了第 i 个人,那么为了不打草惊蛇,就不能封禁他的关注者 ai。现在想知道最多可以封禁多少个 xxs。

输入第一行是一个不超过 300000 的整数 n,第二行是 n1n 的整数表示 ai

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

#include <cstdio>
using namespace std;
#define MAXN 300005
int n, ans = 0, a[MAXN], in[MAXN] = {0};
bool vis[MAXN] = {0};
void dfs(int cur, int w) {
   if(vis[cur])
       return;
   vis[cur] = true;
   if(w == 1) ans++;
   ①
   if(②)
       dfs(a[cur], ③);
}
int main() {
   scanf("%d", &n);
   for(int i = 1; i <= n; i++) {
       scanf("%d", &a[i]);
       in[a[i]]++;
   }
   for(int i = 1; i <= n; i++)
       if(!in[i]) ④;
   for(int i = 1; i <= n; i++)
       if(⑤) dfs(i, 0);
   printf("%d\n", ans);
   return 0;
}

1.①处应填( )

A. a[cur]=cur;       B. in[a[cur]]=0;       C. in[a[cur]]--;       D. in[cur]--;

2.②处应填( )

A. in[a[cur]]!=0||w == 1       B. in[a[cur]]==0||w == 0       C. in[a[cur]]!=0||w == 0       D. in[a[cur]]==0||w == 1

3.③处应填( )

A. 0       B. 1 C. w D. 1-w

4.④处应填( )

A. dfs(i,1)       B. dfs(i,0)       C. dfs(a[i],1)       D. dfs(a[i],0)

5.⑤处应填( )

A. !in[i]       B. in[i]       C. !vis[i]       D. vis[i]

点击查看答案
分析题目中哪些条件下处理简单:
首先, 若只有一条链则交替封禁xxs就可以简单实现
而若产生两条链交叉,则可以在交叉点分成两条链式结构.
而复杂问题是会出现环状图形.当产生环之后就需要考虑破环成链从某一结点入手(考虑不举报这个人就可以直接破环)
此时核心的分类已经讨论完成了,那么只需要按照思路思考即可:
首先主函数内显然21-22,与23-24在做分类讨论: 圈4就显然是的dfs(i,1)
而圈5的含义应表示没有考虑过的点,而代码用vis数组记录是否搜索过所以应为!vis[i]
圈2的就是考虑什么情况可以搜索即上文分析的(非环且不交叉)或(环上不举报的点), 转换成代码含义即!in[a[cur]] or w == 1
同时根据交叉举报的原则圈3 w^=1 等价于 w = 1-w
圈1就简单得到要更改图上信息即 in[cur]--

二十、
(烧作业)某课作业布置了 N(3N100000) 个题目,第 i 题对应的得分是 ai。作业的总得分的计算方式为去掉作业中得分最小的一个题,剩下其它所有题目得分的平均值。但很不幸小 A 遇到了一场火灾,前 K(1KN2) 个题目被烧了,无法记录得分。小 A 想知道,K 是多少时,可以得到最高的作业得分? 作业被烧了前 K 页,这时的得分是从第 K+1 页到最后一页中,去除最小得分后取平均值。

输入第一行是整数 N,第二行是 n 个不超过 10000 的非负整数表示 ai

输出一行,若干个整数表示答案。如果有多个 K,请依次升序输出。

#include <cstdio>
#include <cmath>
#define min(a,b) (a<b?a:b)
#define MAXN 100002
using namespace std;
int n, k[MAXN], cnt = 0;
int s[MAXN], minScore, sum;
double maxAverage = 0, nowAverage;
int main() {
   scanf("%d", &n);
   for(int i = 1; i <= n; i++)
       scanf("%d", &s[i]);
   minScore = s[n];
   ①;
   for(int i = n - 1; i >= 2; i--) {
       minScore = min(minScore, s[i]);
       ②;
       nowAverage = ③;
       if(nowAverage > maxAverage) {
           ④
           maxAverage = nowAverage;
       } else if(fabs(nowAverage - maxAverage) < 1e-6)
           ⑤;
   }
   for(int i = cnt; i >= 1; i--)
       printf("%d\n", k[i]);
   return 0;
}

1.①处应填( )

A. sum=n       B. sum=s[1]       C. sum=s[n]       D. sum=0

2.②处应填( )

A. sum=maxAverage*(n-i)       B. sum+=s[i]       C. sum+=s[n-i]       D. sum=s[i]+minScore

3.③处应填( )

A. (double)(sum+minScore)/(n-i)       B. sum*1.0/(n-i)       C. (int)(sum-minScore)/(n-i)       D. (double)(sum-minScore)/(n-i)

4.④处应填( )

A. k[++cnt]=i;       B. k[cnt++]=i-1       C. cnt=1;k[cnt]=i-1;       D. cnt=0;k[cnt]=i;

5.⑤处应填( )

A. k[cnt++]=i;       B. k[++cnt]=i-1;       C. k[cnt++]=n-i;       D. k[cnt]=i;

点击查看答案
简单总结目标是:求后缀平均数最大
从后向前以此计算,需要处理的就是后缀和,后缀最小值 即sum,minScore
根据第15行的循环起点i = n-1 那么sum应初始为最后一个数即 s[n]
而后缀和sum的处理相对简单,无需具体讲解
圈3的题目 C向 转int 会导致结果精度不准确影响答案, 所以要用double类型
圈4要清空重新统计答案,但由于k[cnt]的写法,cnt不应清空. 且考虑是清空前k个作业,故不应包括i
圈5应使用 ++cnt 去统计数组位置, 填入数字原则同上
posted @   nomonick  阅读(913)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示