第八届蓝桥杯(软件类)省赛C++B组真题题解


注:本文中未列写出来的题和省赛A组的题相同,可自行前往A组题解查阅。博客链接

B组真题(其余题与A组题相同)

题目结构

题目类型分值
第一题结果填空5分
第二题结果填空7分
第三题结果填空13分
第四题结果填空17分
第五题代码填空9分
第六题代码填空11分
第七题程序设计19分
第八题程序设计21分
第九题程序设计23分
第十题程序设计25分

第一题 购物单

  • 问题重现

    小明刚刚找到工作,老板人很好,只是老板夫人很爱购物。
    老板忙的时候经常让小明帮忙到商场代为购物。小明很厌烦,但又不好推辞。
    这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折优惠的。
    小明也有个怪癖,不到万不得已,从不刷卡,直接现金搞定。
    现在小明很心烦,请你帮他计算一下,需要从取款机上取多少现金,才能搞定这次购物。
    取款机只能提供100元面额的纸币。小明想尽可能少取些现金,够用就行了。
    你的任务是计算出,小明最少需要取多少现金。

    输入

    本题无输入,购物单如下,物品名称被隐藏了。

    ****     180.90       88****      10.25       65****      56.14        9****     104.65        9****     100.30       88****     297.15        半价
    ****      26.75       65****     130.62        半价
    ****     240.28       58****     270.62        8****     115.87       88****     247.34       95****      73.21        9****     101.00        半价
    ****      79.54        半价
    ****     278.44        7****     199.26        半价
    ****      12.97        9****     166.30       78****     125.50       58****      84.98        9****     113.35       68****     166.57        半价
    ****      42.56        9****      81.90       95****     131.78        8****     255.89       78****     109.17        9****     146.69       68****     139.33       65****     141.16       78****     154.74        8****      59.42        8****      85.44       68****     293.70       88****     261.79       65****      11.30       88****     268.27       58****     128.29       88****     251.03        8****     208.39       75****     128.88       75****      62.06        9****     225.87       75****      12.89       75****      34.28       75****      62.16       58****     129.12        半价
    ****     218.37        半价
    ****     289.69        8

    需要说明的是,88折指的是按标价的88%计算,而8折是按80%计算,余者类推。
    特别地,半价是按50%计算。

    输出

    输出一个整数表示小明要从取款机上提取的金额,单位是元。

  • 解题思路

    我们应该都知道,怎么求,但可能对数据处理这里就要花一点思路了,我一开始是直接手动将这 50 50 50个值放入 p r i c e price price数组和 d i s c o u n t discount discount数组中的。这种方法你必须确保自己十分细分,不会落了数据等问题。还有一种方法就是利用 E x c e l Excel Excel表来求解了,这种方法既容易实现又不容易出错。方法如下:

    • 首先,我们需要新建一个文本文档将数据复制过来,然后利用 C t r l + H Ctrl+H Ctrl+H文本替换功能将****替换,还有将半价替换为 50 50 50,将折去掉。得到如下:

在这里插入图片描述

  • 然后,将这些数据放入 E x c e l Excel Excel表中,我们发现,这数据都到一列中去了:

在这里插入图片描述

  • 这并不是我们想要的,所以我们需要进行分列操作,这个功能在数据版块下:
    在这里插入图片描述

  • 我们选择空格作为分隔符号则可得到如下的数据:

在这里插入图片描述

  • 最后,将其中的个位数换成十位数,即将 8 8 8折这种换成 80 80 80,显然这不需要很久,我们最后直接利用公式计算, = B : B ∗ C : C ∗ 0.01 =B:B*C:C*0.01 =B:BC:C0.01得到价格,最后利用 s u m sum sum函数,即 = S U M ( D 1 : D 50 ) =SUM(D1:D50) =SUM(D1:D50),就可以得到结果。如下:

    在这里插入图片描述

所以蓝桥杯一定要多利用好这些工具,会省下不少的麻烦。

  • 代码
/**
  *@filename:购物单
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-03-31 13:21
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1000 + 5;
const int mod = 1e9+7;

//复制数据到数组中
float price[maxn]={180.90,10.25,56.14,104.65,
100.30,297.15,26.75,130.62,240.28,270.62,115.87,
247.34,73.21,101.00,79.54,278.44,199.26,12.97,
166.30,125.50,84.98,113.35,166.57,42.56,81.90,131.78,
255.89,109.17,146.69,139.33,141.16,154.74,59.42,
85.44,293.70,261.79,11.30,268.27,128.29,251.03,
208.39,128.88,62.06,225.87,12.89,34.28,62.16,129.12,
218.37,289.69};
float discount[maxn]={0.88,0.65,0.9,0.9,0.88,0.5,0.65,0.5,0.58,0.8,0.88,
0.95,0.9,0.5,0.5,0.7,0.5,0.9,0.78,0.58,0.9,0.68,0.5,0.9,0.95,0.8,0.78,0.9,
0.68,0.65,0.78,0.8,0.8,0.68,0.88,0.65,0.88,0.58,0.88,0.8,0.75,0.75,0.9,0.75,
0.75,0.75,0.58,0.5,0.5,0.8};         
int n;
void solve(){
    float ans=0;
    for(int i=0;i<50;i++){
        ans+=price[i]*discount[i];
    }
    //直接打印,手动判断需要多少。
    cout<<ans<<endl;//5136.86
}
int main() {
    solve();
    return 0;
}
  • 答案

    5200 。 5200。 5200


第二题 等差素数列

  • 问题重现

    2,3,5,7,11,13,…是素数序列。
    类似:7,37,67,97,127,157 这样全由素数组成的等差数列,叫等差素数数列。
    上边的数列公差为30,长度为6。
    2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
    这是数论领域一项惊人的成果!
    有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:
    长度为10的等差素数列,其公差最小值是多少?

    输出

    输出一个整数表示答案

  • 解题思路

    先利用素数筛将所有的素数找出来,然后暴力枚举公差和数列首项。

  • 代码

/**
  *@filename:等差素数�?
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-03-31 13:52
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1000000 + 5;
const int mod = 1e9+7;

//枚举公差即可。当然我们也需要建一个素数表
bool isprimer[maxn];
int primer[maxn];
int len;
void init(){
    memset(isprimer,true,sizeof(isprimer));
    int temp=sqrt(maxn);
    for(int i=2;i<=temp;i++){
        if(isprimer[i]){
            primer[len++]=i;
            for(int j=i*i;j<=maxn;j+=i){
                isprimer[j]=false;
            }
        }
    }
}
bool check(int index,int d){
    int temp=primer[index]+9*d;//确定尾数
    if(temp>=maxn)return false;
    for(int i=primer[index];i<=temp;i+=d){
        if(!isprimer[i]){
            return false;
        }
    }
    return true;
}
void solve(){
    bool flag=false;
    for(int d=1;d<=400;d++){
        //枚举首项?
        for(int index=0;index<len-10;index++){
            if(check(index,d)){
                cout<<"首项为:"<<primer[index]<<"公差为:"<<d<<endl;//199 210.
                flag=true;
                break;
            }
        }
        if(flag)break;
    }
}
int main() {
    len=0;
    init();
    solve();
    return 0;
}
  • 答案

    210 210 210


第三题 承压计算

  • 问题重现

    X星球的高科技实验室中整齐地堆放着某批珍贵金属原料。
    每块金属原料的外形、尺寸完全一致,但重量不同。
    金属材料被严格地堆放成金字塔形。
    其中的数字代表金属块的重量(计量单位较大)。(参考输入中的金字塔)
    最下一层的X代表30台极高精度的电子秤。
    假设每块原料的重量都十分精确地平均落在下方的两个金属块上,
    最后,所有的金属块的重量都严格精确地平分落在最底层的电子秤上。
    电子秤的计量单位很小,所以显示的数字很大。
    工作人员发现,其中读数最小的电子秤的示数为:2086458231
    请你推算出:读数最大的电子秤的示数为多少?

    输入

    本题无输入

                                7 
                               5 8 
                              7 8 8 
                             9 2 7 2 
                            8 1 4 9 1 
                           8 1 8 8 4 1 
                          7 9 6 1 4 5 4 
                         5 6 5 5 6 9 5 6 
                        5 5 4 7 9 3 5 5 1 
                       7 5 7 9 7 4 7 3 3 1 
                      4 6 4 5 5 8 8 3 2 4 3 
                     1 1 3 3 1 6 6 5 5 4 4 2 
                    9 9 9 2 1 9 1 9 2 9 5 7 9 
                   4 3 3 7 7 9 3 6 1 3 8 8 3 7 
                  3 6 8 1 5 3 9 5 8 3 8 1 8 3 3 
                 8 3 2 3 3 5 5 8 5 4 2 8 6 7 6 9 
                8 1 8 1 8 4 6 2 2 1 7 9 4 2 3 3 4 
               2 8 4 2 2 9 9 2 8 3 4 9 6 3 9 4 6 9 
              7 9 7 4 9 7 6 6 2 8 9 4 1 8 1 7 2 1 6 
             9 2 8 6 4 2 7 9 5 4 1 2 5 1 7 3 9 8 3 3 
            5 2 1 6 7 9 3 2 8 9 5 5 6 6 6 2 1 8 7 9 9 
           6 7 1 8 8 7 5 3 6 5 4 7 3 4 6 7 8 1 3 2 7 4 
          2 2 6 3 5 3 4 9 2 4 5 7 6 6 3 2 7 2 4 8 5 5 4 
         7 4 4 5 8 3 3 8 1 8 6 3 2 1 6 2 6 4 6 3 8 2 9 6 
        1 2 4 1 3 3 5 3 4 9 6 3 8 6 5 9 1 5 3 2 6 8 8 5 3 
       2 2 7 9 3 3 2 8 6 9 8 4 4 9 5 8 2 6 3 4 8 4 9 3 8 8 
      7 7 7 9 7 5 2 7 9 2 5 1 9 2 6 5 3 9 3 5 7 3 5 4 2 8 9 
     7 7 6 6 8 7 5 5 8 2 4 7 7 4 7 2 6 9 2 1 8 2 9 8 5 7 3 6 
    5 9 4 5 5 7 5 5 6 3 5 3 9 5 8 9 5 4 1 2 6 1 4 3 5 3 2 4 1 
    X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X
    

    输出

    输出一个整数表示答案

  • 解题思路

    这道题其实并不难,我们很容易就发现每一层的第一列和最后一列的重量分得只有一个,其余列都是两个(当然除了第一层)。所以,我们可以直接从上往下更新重量,特判特殊的列即可。当然电子秤上的数值是被放大了的,所以我们需要将这比重算出来再乘以最大值即可得到我们的答案。

    PS:对于这种大数据确实有点烦

  • 代码

/**
  *@filename:承压计算
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-03-31 14:12
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100 + 5;
const int mod = 1e9+7;

//共29行。
double a[maxn][maxn]={
{7,0},
{5,8,0},
{7,8,8,0},
{9,2,7,2,0},
{8,1,4,9,1,0},
{8,1,8,8,4,1,0},
{7,9,6,1,4,5,4,0},
{5,6,5,5,6,9,5,6,0},
{5,5,4,7,9,3,5,5,1,0},
{7,5,7,9,7,4,7,3,3,1,0},
{4,6,4,5,5,8,8,3,2,4,3,0},
{1,1,3,3,1,6,6,5,5,4,4,2,0},
{9,9,9,2,1,9,1,9,2,9,5,7,9,0},
{4,3,3,7,7,9,3,6,1,3,8,8,3,7,0},
{3,6,8,1,5,3,9,5,8,3,8,1,8,3,3,0},
{8,3,2,3,3,5,5,8,5,4,2,8,6,7,6,9,0},
{8,1,8,1,8,4,6,2,2,1,7,9,4,2,3,3,4,0},
{2,8,4,2,2,9,9,2,8,3,4,9,6,3,9,4,6,9,0},
{7,9,7,4,9,7,6,6,2,8,9,4,1,8,1,7,2,1,6,0},
{9,2,8,6,4,2,7,9,5,4,1,2,5,1,7,3,9,8,3,3,0},
{5,2,1,6,7,9,3,2,8,9,5,5,6,6,6,2,1,8,7,9,9,0},
{6,7,1,8,8,7,5,3,6,5,4,7,3,4,6,7,8,1,3,2,7,4,0},
{2,2,6,3,5,3,4,9,2,4,5,7,6,6,3,2,7,2,4,8,5,5,4,0},
{7,4,4,5,8,3,3,8,1,8,6,3,2,1,6,2,6,4,6,3,8,2,9,6,0},
{1,2,4,1,3,3,5,3,4,9,6,3,8,6,5,9,1,5,3,2,6,8,8,5,3,0},
{2,2,7,9,3,3,2,8,6,9,8,4,4,9,5,8,2,6,3,4,8,4,9,3,8,8,0},
{7,7,7,9,7,5,2,7,9,2,5,1,9,2,6,5,3,9,3,5,7,3,5,4,2,8,9,0},
{7,7,6,6,8,7,5,5,8,2,4,7,7,4,7,2,6,9,2,1,8,2,9,8,5,7,3,6,0},
{5,9,4,5,5,7,5,5,6,3,5,3,9,5,8,9,5,4,1,2,6,1,4,3,5,3,2,4,1,0},
};
int n;
void solve(){
    //开始平均分。我们首先要知道,只有第一列和最后一列是特殊的,即只能继承一个材料的一半。
    for(int i=1;i<=29;i++){
        for(int j=0;j<=i;j++){
            if(j==0){
                a[i][j]=a[i][j]+(a[i-1][j])/2.0;
            }
            else if(j==i){
                a[i][j]=a[i][j]+(a[i-1][j-1])/2.0;
            }
            else{
                a[i][j]=a[i][j]+(a[i-1][j-1]+a[i-1][j])/2.0;
            }
        }
    }
    double minn=1000000000,maxx=0;
    for(int j=0;j<=29;j++){
        minn=min(a[29][j],minn),maxx=max(a[29][j],maxx);
    }
    cout<<minn<<" "<<maxx<<endl;
    cout<<(ll)(maxx*2086458231/minn)<<endl;//72665192664
}
int main() {
    solve();
    return 0;
}
  • 答案

    72665192664 72665192664 72665192664


第五题 取数位

  • 问题重现

    求1个整数的第k位数字有很多种方法。
    以下的方法就是一种。

    // 求x用10进制表示时的数位长度 
    int len(int x){
    if(x<10) return 1;
    return len(x/10)+1;
    }
    
    // 取x的第k位数字
    int f(int x, int k){
    if(len(x)-k==0) return x%10;
    return _____________________;  //填空
    }
    
    int main()
    {
    int x = 23574;
    printf("%d\n", f(x,3));
    return 0;
    }
    

    对于题目中的测试数据,应该打印5。
    请仔细分析源码,并补充划线部分所缺少的代码。
    注意:只提交缺失的代码,不要填写任何已有内容或说明性的文字。

  • 解题思路

    就是一道递归求解问题,我们观察第一条语句就极易得出。

  • 答案

f(x/10,k)

第七题 日期问题

  • 问题重现

    小明正在整理一批历史文献。这些历史文献中出现了很多日期。
    小明知道这些日期都在1960年1月1日至2059年12月31日。
    令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。
    更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。
    比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
    给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

    输入

    一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)

    输出

    输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。
    多个日期按从早到晚排列。

    样例输入

    02/03/04

    样例输出

    2002-03-04
    2004-02-03
    2004-03-02

  • 解题思路

    一道模拟题,按照题意组合日期即可,要注意的就是日期顺序排列以及一些情况判断,比如判断是否是闰年,判断日期是否已经出现(即判重)。

  • 代码

/**
  *@filename:日期问题
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-03-31 14:59
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

int months[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
struct Date{
    int year,month,day;
};
int x,y,z;
vector<Date> date;//存放日期。
bool cmp(Date a,Date b){
    if(a.year==b.year){
        if(a.month==b.month){
            return a.day<b.day;
        }
        return a.month<b.month;
    }
    return a.year<b.year;
}
bool isSpecial(int year){
    if((year%4==0&&year%100!=0)||year%400==0){
        return true;
    }
    else{
        return false;
    }
}
void join(int year,int month,int day){
    if(year>=60) year+=1900;
    else year+=2000;
    if(isSpecial(year)) months[2]=29;
    else months[2]=28;
    //接下来开始判断满足条件。
    if(month>0&&month<=12&&day>0&&day<=months[month]){
        //去重
        for(int i=0;i<date.size();i++){
            if(year==date[i].year&&month==date[i].month&&day==date[i].day){
                return;
            }
        }
        date.push_back({year,month,day});
    }
}
void solve(){
    //我们分情况判断是否符合即可。
    //年月日 日月年 月日年。
    int year,month,day;
    //x,y,z
    year=x,month=y,day=z;
    join(year,month,day);
    //z y x
    year=z,month=y,day=x;
    join(year,month,day);
    //z x y
    year=z,month=x,day=y;
    join(year,month,day);
    sort(date.begin(),date.end(),cmp);
    for(auto &x:date){
        printf("%04d-%02d-%02d\n",x.year,x.month,x.day);
    }
}
int main() {
    scanf("%d/%d/%d",&x,&y,&z);
    solve();
    return 0;
}

第十题 k倍区间

  • 问题重现

    给定一个长度为N的数列,A1, A2, … AN。
    如果其中一段连续的子序列Ai, Ai+1, … Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
    你能求出数列中总共有多少个K倍区间吗?

    输入

    第一行包含两个整数N和K。(1 <= N, K <= 100000)
    以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)

    输出

    输出一个整数,代表K倍区间的数目。

    样例输入

    5 2
    1
    2
    3
    4
    5

    样例输出

    6

  • 解题思路
    这道题和 c o d e f o r c e s codeforces codeforces上的一道题有些类似。链接

    一开始我们可能都会想到利用前缀和暴力来解决这个问题,但不难发现,这样做的时间复杂度为 O ( n 2 ) O(n^2) O(n2),显然会超时。所以解法肯定不是这样做的,那么该如何处理呢?

    这道题其实我们是只关心前缀和对 k k k的余数,因为如果 p r e [ i ] pre[i] pre[i]的余数和 p r e [ j ] pre[j] pre[j]的余数相等的话 ( i < j ) (i<j) (i<j),那么 ( i , j ] (i,j] (i,j]之间的数相加即为 p r e [ i ] − p r e [ j ] pre[i]-pre[j] pre[i]pre[j],这个余数一定是 0 0 0,也就是说能够整除 k k k 所以我们只需要在遍历过程中记录前缀和对 k k k的余数是否出现过,如果出现同样的前缀和余数,那么这两个前缀和的差集的余数自然是为 0 0 0的,故此题可解。值得注意的是,因为首次出现 0 0 0的前缀和它自身原本就是能够整除 k k k,所以为了记录这种特性,我们需要将 m a p map map容器中的 0 0 0键值初始化为 1 1 1

  • 代码

/**
  *@filename:K倍区间
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-03-31 15:38
**/
/*
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

//立马想到利用前缀和去写。
ll pre[maxn];
int n,k;
void solve(){
    //枚举起点和长度,统计。
    ll ans=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j+i<=n;j++){
            if((pre[i+j]-pre[j])%k==0){
                ans++;
            }
        }
    }
    cout<<ans<<endl;
}
int main() {
    while(cin>>n>>k){
        int temp;
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=n;i++){
            cin>>temp;
            pre[i]=pre[i-1]+temp;//初始化前缀和。
        }
        solve();
    }
    return 0;
}*/
/**
  *@filename:K倍区间
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-03-31 16:21
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

int pre[maxn];//存储前缀和。
map<int,int> p;//标记余数的出现次数。
int n,k;
int main() {
    while(cin>>n>>k){
        int temp;
        ll ans=0;
        p[0]=1;//由于余数为0的可以自己凑成,所以我们这里考虑将这里标记为1.
        pre[0]=0;
        for(int i=1;i<=n;i++){
            cin>>temp;
            pre[i]=(pre[i-1]+temp)%k;
            ans+=p[pre[i]];//两个余数相等的前缀和就可以凑成一个k倍区间。
            p[pre[i]]++;
        }
        cout<<ans<<endl;
    }
    return 0;
}
posted @ 2022-03-26 16:49  unique_pursuit  阅读(31)  评论(0)    收藏  举报