2020 第十一届蓝桥杯大赛软件赛省赛(第一场),C/C++大学B组题解

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020 第十一届蓝桥杯大赛软件赛省赛(第一场),C/C++大学B组题解相关的知识,希望对你有一定的参考价值。

第1题 —— 跑步训练 (5分)

  • 题目:
    小明要做一个跑步训练。
    初始时,小明充满体力,体力值计为10000。如果小明跑步,每分钟损耗600 的体力。如果小明休息,每分钟增加300 的体力。体力的损耗和增加都是均匀变化的。
    小明打算跑一分钟、休息一分钟、再跑一分钟、再休息一分钟……如此循环。如果某个时刻小明的体力到达0,他就停止锻炼。
    请问小明在多久后停止锻炼。为了使答案为整数,请以秒为单位输出答案。
    答案中只填写数,不填写单位。

  • 首先求每秒的消耗,然后模拟跑步休息的过程即可

  • 答案是3880

#include<bits/stdc++.h>
using namespace std;

int main()
    int n = 10000;
    int v = 600/60;
    int minues = 0;
    while(n)
        if(n-600<0)
            cout<<minues*60+n/v<<"\\n";
            return 0;
        
        n -= 600;
        n += 300;
        minues += 2;
    
    return 0;



第2题 —— 纪念日 (5分)

  • 题目:
    2020 年7 月1 日是中国共产党成立99 周年纪念日。
    中国共产党成立于1921 年7 月23 日。
    请问从1921 年7 月23 日中午12 时到2020 年7 月1 日中午12 时一共包含多少分钟?

  • 直接excel

  • 也可以枚举每一年,手动判断闰年累加

#include<bits/stdc++.h>
using namespace std;

int main()
    int res = 0;
    for(int i = 1922; i <= 2020; i++)
        if(i%400==0 || (i%100!=0&&i%4==0))res += 366;
        else res += 365;
    
    res -= 22;
    cout<<res*24*60;
    return 0;



  • 答案 52038720

第3题 —— 合并检测 (10分)

  • 题目
    新冠疫情由新冠病毒引起,最近在 A 国蔓延,为了尽快控制疫情, A 国准备给大量民众进病毒核酸检测。
    然而,用于检测的试剂盒紧缺。
    为了解决这一困难,科学家想了一个办法:合并检测。即将从多个人( k 个)采集的标本放到同一个试剂盒中进行检测。如果结果为阴性,则说明这 k 个人都是阴性,用一个试剂盒完成了 k 个人的检测。如果结果为阳性,则说明至少有一个人为阳性,需要将这 k 个人的样本全部重新独立检测(从理论上看,如果检测前 k−1 个人都是阴性可以推断出第 k 个人是阳性,但是在实际操作中不会利用此推断,而是将 k 个人独立检测),加上最开始的合并检测,一共使用了 k+1 个试剂盒完成了 k 个人的检测。
    A 国估计被测的民众的感染率大概是 1,呈均匀分布。请问 k 取多少能最节省试剂盒?
  • 感染率为1,设测试人员为100,则1人感染,当k可以被100整除时则100/k+k,不可以被100整除则100/k+k+1。暴力枚举1-100,更新最小的k。
  • 答案是10
#include<bits/stdc++.h>
using namespace std;

int main()
    int kk, res = 100;
    for(int k = 1; k <= 100; k++)
        if(100%k==0)
            int t = 100/k+k;
            if(t<res)kk=k, res=t;
        else
            int t = 100/k+k+1;
            if(t<res)kk=k,res=t;
        
    
    cout<<kk<<"\\n";
    return 0;


第4题 —— REPEAT程序 (10分)

  • 题目:
    附件 prog.txt 中是一个用某种语言写的程序。
    其中 REPEAT k 表示一个次数为 k 的循环。循环控制的范围由缩进表达,从次行开始连续的缩进比该行多的(前面的空白更长的)为循环包含的内容。
    例如如下片段:
    REPEAT 2:
     A = A + 4
      REPEAT 5:
      REPEAT 6:
       A = A + 5
      A = A + 7
     A = A + 8
    A = A + 9
    该片段中从 A = A + 4 所在的行到 A = A + 8 所在的行都在第一行的循环两次中。
    REPEAT 6: 所在的行到 A = A + 7 所在的行都在 REPEAT 5: 循环中。
    A = A + 5 实际总共的循环次数是 2 × 5 × 6 = 60 次。
    请问该程序执行完毕之后,A 的值是多少?

  • 把repeat换成for模拟即可。

  • 答案是403

#include<bits/stdc++.h>
using namespace std;

int main()
    int res = 0;
    for(int i = 0; i < 2; i++)
        res += 4;
        for(int j = 0; j < 5; j++)
            for(int k = 0; k < 6; k++)
                res += 5;
            
            res += 7;
        
        res += 8;
    
    res += 9;
    cout<<res<<"\\n";
    return 0;



第5题 —— 矩阵 (15分)

  • 题目
    把 1∼2020 放在 2×1010 的矩阵里。要求同一行中右边的比左边大,同一列中下边的比上边的大。一共有多少种方案?
    答案很大,你只需要给出方案数除以 2020 的余数即可。
  • 令fij表示第一行放了i个数,第二行放了j个数时的方案数。
    初始状态为两行一个数字都不放f00算一种方案。
    转移时考虑把当前数第i+j个数,放到第一行或第二行,对应的+=fi-1j或fij-1即可。
  • 也可以考虑放了一个数以后,下一个数一定放在右边或下面一行,且下面一行满足所有时刻数量要小于第一行的,不然后面就会有数不得不被放到第一行去。
  • 答案:1340
#include<bits/stdc++.h>
using namespace std;
int f[1020][1020];

int main()
    f[0][0] = 1;
    for(int i = 0; i <= 1010; i++)
        for(int j = 0; j <= 1010; j++)
            if(j+1 <= i)f[i][j] += f[i-1][j]%2020;
            if(j>0)f[i][j] += f[i][j-1]%2020;
        
    
    cout<<f[1010][1010];
    return 0;



第6题 —— 整除序列 (15分)

  • 题目:
    有一个序列,序列的第一个数是n,后面的每个数是前一个数整除2,请输
    出这个序列中值为正数的项。
  • 每次除2就行,即可开ll
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int main()
    LL n;  cin>>n;
    while(n)
        cout<<n<<" ";
        n /= 2;
    
    return 0;



第7题 —— 解码 (20分)

  • 题目
    小明有一串很长的英文字母,可能包含大写和小写。
    在这串字母中,有很多连续的是重复的。小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母+ 出现次数的形式。
    例如,连续的5 个a,即aaaaa,小明可以简写成a5(也可能简写成a4a、aa3a 等)。
    对于这个例子:HHHellllloo,小明可以简写成H3el5o2。为了方便表达,小明不会将连续的超过9 个相同的字符写成简写的形式。
    现在给出简写后的字符串,请帮助小明还原成原来的串。

  • 对于输入的字符串模拟即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int main()
    string s;  cin>>s;
    for(int i = 0; i < s.size(); i++)
        if(isalpha(s[i]) && isdigit(s[i+1]))
            cout<<string(s[i+1]-'0', s[i]);
            i++;
        else
            cout<<s[i];
        
    
    return 0;



第8题 —— 走方格 (20分)

  • 题目:
    在平面上有一些二维的点阵。
    这些点的编号就像二维数组的编号一样,从上到下依次为第1 至第n 行,从左到右依次为第1 至第m 列,每一个点可以用行号和列号来表示。
    现在有个人站在第1 行第1 列,要走到第n 行第m 列。只能向右或者向下走。
    注意,如果行号和列数都是偶数,不能走入这一格中。
    问有多少种方案。

  • 只能向下或向右走,所以转移为左边或上面的加起来,状态fij为到ij的方案数。

  • 对于行号列号为偶数,不能走的点,直接加个if不加上去就行

#include<bits/stdc++.h>
using namespace std;
int f[110][110];

int main()
    int n, m;  cin>>n>>m;
    for(int i = 1; i <= n; i++)f[i][1] = 1;
    for(int i = 1; i <= m; i++)f[1][i] = 1;
    for(int i = 2; i <= n; i++)
        for(int j = 2; j <= m; j++)
            if(i%2==0 && j%2==0)continue;
            f[i][j] = f[i-1][j]+f[i][j-1];
        
    
    cout<<f[n][m];
    return 0;



第9题 —— 整数拼接 (25分)

  • 题目
    给定一个长度为 n 的数组 A1,A2,⋅⋅⋅,An。
    你可以从中选出两个数 Ai 和 Aj(i 不等于 j),然后将 Ai 和 Aj 一前一后拼成一个新的整数。
    例如 12 和 345 可以拼成 12345 或 34512。
    注意交换 Ai 和 Aj 的顺序总是被视为 2 种拼法,即便是 Ai=Aj 时。
    请你计算有多少种拼法满足拼出的整数是 K 的倍数。

  • 考虑若a,b拼接,a放在前面,则产生的新的a最后也就*10^20,最后20个数,即将n的范围乘20以后,可以得到所有的数,问题转化为20000个数选两个相加%k==0, 可以直接预处理出%k的余数,枚举对于每个数(指定后缀长度)后,相加能得到的k的倍数的个数累加即可。

  • 预处理 cnt[len][x] 表示满足 a[i] ∗ 10^len % k = x 的 i 的个数

  • 对于每个 a[j] ,找满足 a[i] ∗ 10^len(a[j]) % k = ( k − a[j] % k ) % k 的 i 的个数即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 1e5+10;

LL a[maxn];
LL p[20], cnt[20][maxn];

int main()
    LL n, k;  cin>>n>>k;
    for(int i=1; i <= n; i++)cin>>a[i];
    for(int i = 1; i < 20; i++)p[i]=p[i-1]*10%k;
    for(int i = 1; i <= n; i++)//对于每个数
        for(int len = 0; len < 20; len++)//分别乘1~20以后,%k的余数
            cnt[len][a[i]*p[len]%k]++;
        
    
    LL res = 0;
    for(int i = 1; i <= n; i++)
        for(int len=0; len<20; len++)cnt[len][a[i]*p[len]%k]--;//自己不能放到自己前面
        int len = to_string(a[i]).size();
        res += cnt[len][((k-a[i])%k+k)%k]; //a[i]放在后面,len为长度,k-a[i]的个数
        for(int len=0; len<20; len++)cnt[len][a[i]*p[len]%k]++;
    
    cout<<res<<"\\n";
    return 0;



第10题 —— 网络分析 (25分)

  • 题目
    小明正在做一个网络实验。
    他设置了 n 台电脑,称为节点,用于收发和存储数据。
    初始时,所有节点都是独立的,不存在任何连接。
    小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。两个节点如果存在网线连接,称为相邻。
    小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。所有发送和接收的节点都会将信息存储下来。一条信息只存储一次。
    给出小明连接和测试的过程,请计算出每个节点存储信息的大小。

  • n个点,有些点相连,从某点发信息,能联通的点信息+1,求m次操作以后,每个点的信息大小

  • 并查集板子

  • 对于每次联通块加一个值,如果遍历出所有的联通块,可能会超时,所以可以打个懒标记lazy,每次合并前把每个节点的值都更新一下即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 1e5+10;

int n, m;
int v[maxn], lazy[maxn]; 

int fa[maxn+10];
void init(int n)for(int i = 0; i <= n; i++)fa[i]=i;
int find(int x)return x==fa[x]?x:fa[x]=find(fa[x]);
void merge(int x, int y)
    x=find(x);y=find(y);
    if(x!=y)
        for(int i = 1; i <= n; i++)//合并前把每个节点的值都更新一下,pushdown
            v[i] += lazy[find(i)];
        
        memset(lazy,0,sizeof(lazy));
        fa[x]=y;
    


int main()
    cin>>n>>m;
    init(maxn-10);
    for(int i = 1; i <= m; i++)
        int a, b, c;  cin>>a>>b>>c;
        if(a==1)
            merge(b,c);
        else
            lazy[find(b)] += c;//联通块b+=c, 延迟更新
        
    
    for(int i = 1; i <= n; i++)
        cout<<v[i]+lazy[find(i)]<<" ";
    
    return 0;



以上是关于2020 第十一届蓝桥杯大赛软件赛省赛(第一场),C/C++大学B组题解的主要内容,如果未能解决你的问题,请参考以下文章

2020 第十一届蓝桥杯大赛软件赛省赛(第二场),C/C++大学B组题解

2021.5.9 第十二届蓝桥杯大赛软件赛省赛第二场大学B组(个人题解)

第十三届蓝桥杯大赛软件赛省赛 C/C++ 大学 B 组思考+总结

第十二届蓝桥杯大赛软件赛省赛第二场C++B组

2022 第十三届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解

2022 第十三届蓝桥杯大赛软件赛省赛(第二场),C/C++ 大学B组题解