2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)

C++省赛B组个人题解(部分

目录

  • 试题A:门牌制作
  • 试题B:既约分数
  • 试题C:蛇形填数
  • 试题D:跑步锻炼
  • 试题E:七段码
  • 试题F:成绩统计
  • 试题G:回文日期
  • 试题H:子串分值和
  • 试题I:平面切分
  • 试题J:冒泡排序
  • 个人总结:

试题A:门牌制作

题目
【问题描述】小蓝要为一条街的住户制作门牌号。这条街一共有2020位住户,门牌号从1到2020编号。小蓝制作门牌的方法是先制作0到9这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌1017需要依次粘贴字符1、0、1、7,即需要1个字符0,2个字符1,1个字符7。请问要制作所有的1到2020号门牌,总共需要多少个字符2?

答案:624

分析
打卡题

#include
using namespace std;
int ans=0;
void f(int n)
{
    while(n)
    {
        if(n%10==2)
        ans++;
        n/=10;
    }
}
int main()
{
    for(int i=1;i<=2020;i++)
    f(i);
    cout<<ans;
    return 0;
}

试题B:既约分数

题目
问题描述】如果一个分数的分子和分母的最大公约数是1,这个分数称为既约分数。例如,3/4,5/2,1/8,7/1都是既约分数。请问,有多少个既约分数,分子和分母都是1到2020之间的整数(包括1和2020)?
【答案提交】这是一道结果填空题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

答案:2481215

分析
求gcd

#include
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){
    return a%b==0?b:gcd(b,a%b);
}
int main()
{
    int ans=0;
    for(ll i=1;i<=2020;i++)
    for(ll j=1;j<=2020;j++)
    {
        if(gcd(i,j)==1)
        ans++;
    }
    cout<<ans;
    return 0;
}

试题C:蛇形填数

题目
【问题描述】
如下图所示,小明用从1开始的正整数“蛇形”填充无限大的矩阵。容易看出矩阵第二行第二列中的数是5。请你计算矩阵中第20行第20列的数是多少?
2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)_第1张图片

答案:761

分析
在考场上没多想,只想到了暴力模拟法。。。斜着看就是奇数行从左到右,偶数行从右到左。不过现在仔细观察,题目求得是20行20列,好像直接公式2*n^2 - 2 * n + 1直接解出来了

#include
using namespace std;
int main()
{
    int a[105][105],i,j,cnt=1;
    for(i=1;i<=100;i++)//分为奇数行和偶数行讨论
    {
        if(i%2)//奇数行 从左到右
        {
            for(j=1;j<=i;j++)
            a[j][i+1-j]=cnt++;
        }
        else//偶数行  从右到左
        {
            for(j=i;j>=1;j--)
            a[j][i+1-j]=cnt++;
        }
    }
    cout<<a[20][20];
    return 0;
}

试题D:跑步锻炼

题目
【问题描述】小蓝每天都锻炼身体。正常情况下,小蓝每天跑1千米。如果某天是周一或者月初(1日),为了激励自己,小蓝要跑2千米。如果同时是周一或月初,小蓝也是跑2千米。小蓝跑步已经坚持了很长时间,从2000年1月1日周六(含)到2020年10月1日周四(含)。请问这段时间小蓝总共跑步多少千米?

答案:8879

分析
求日期,考场上可以打开计算器调到日期模式,计算两个日期的差值,当时直接一天天的加做出来的

#include
using namespace std;
int a[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
}; //数组记录12个月的天数  a[0][m]对应平年,a[1][m]对应闰年
int leapyear(int y)//判断闰年
{
    return y%4==0&&y%100!=0||y%400==0;
}
int days(int y,int m)//返回y年m月的天数
{
    return a[leapyear(y)][m];
}
int main()
{
    int y=2000,m=1,d=1,week=6,cnt=0;
    bool flag=1;
    while(flag)
    {
        if(y==2020&&m==10&&d==1)
        flag=0;

        cnt++;//跑一千米
        if(d==1||week==1)
        cnt++;//额外跑一千米
        //日期处理
        week=week==7?1:(week+1);
        d++;
        if(d>days(y,m))
        {
            d=1;
            m++;
        }
        if(m>12)
        {
            m=1;
            y++;
        }
    }
    cout<<cnt;
    return 0;
}

试题E:七段码

题目
【问题描述】
小蓝要用七段码数码管来表示一种特殊的文字。
七段码上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二极管,分别标记为 a, b, c, d, e, f, g。小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?

emm图片大概长这个样子

2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)_第2张图片

答案:80

分析
考场上想复杂了,当时想着求连通块再计数,最后没做出来,非常可惜。其实直接枚举,然后排除不符合条件的即可。
a的邻居:f,b ;
b的邻居:a,g,c
c的邻居:b,g,d ;
d的邻居:e,c
e的邻居:d,f ;
f的邻居:a,g,e
g的邻居:b,c,e,f
然而然而,要排除3种,abed,afcd,bcef
早知道一开始就自己数数了,反正也就2^7 哈哈哈

#include
using namespace std;
int a,b,c,d,e,f,g,vis[7],ans=0;
int check()
{
    int cnt=0;
    for(int i=0;i<7;i++)
    cnt+=vis[i];
    if(!cnt) return 0;
    else if(cnt==1||cnt==7)  return 1;
    else //有2~6根管子,判断他们的邻居是不是都没亮
    {
        if(a)
        {
            if(!b&&!f) return 0;
        }
        if(b)
        {
            if(!a&&!g&&!c) return 0;
        }
        if(c)
        {
            if(!b&&!g&&!d) return 0;
        }
        if(d)
        {
            if(!e&&!c)  return 0;
        }
        if(e)
        {
            if(!g&&!d&&!f) return 0;
        }
        if(f)
        {
            if(!a&&!g&&!e)  return 0;
        }
        if(g)
        {
            if(!b&&!c&&!e&&!f) return 0;
        }
        return 1;
    }  
}
int main()
{//七重循环
    for(a=0;a<2;a++)
    for(b=0;b<2;b++)
    for(c=0;c<2;c++)
    for(d=0;d<2;d++)
    for(e=0;e<2;e++)
    for(f=0;f<2;f++)
    for(g=0;g<2;g++){
        vis[0]=a;vis[1]=b;vis[2]=c;vis[3]=d;
        vis[4]=e;vis[5]=f;vis[6]=g;
        ans+=check();
    }
    cout<<ans-3;
    return 0;
}

试题F:成绩统计

题目
【问题描述】
小蓝给学生们组织了一场考试,卷面总分为100分,每个学生的得分都是一个0到100的整数。如果得分至少是60分,则称为及格。如果得分至少为85分,则称为优秀。请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整数。
【输入格式】
输入的第一行包含一个整数n,表示考试人数。接下来n行,每行包含一个0至100的整数,表示一个学生的得分。
【输出格式】
输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分四舍五入保留整数。
【样例输入】
7
80
92
56
74
88
100
0
【样例输出】
71%
43%
【评测用例规模与约定】
对于50%的评测用例,1<=n<=100。对于所有评测用例,1<=n<=10000。

分析
四舍五入操作,打卡题

#include
using namespace std;
int main()
{
    int n,a[10005],i,cnt1=0,cnt2=0;
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
        if(a[i]>=60) cnt1++;
        if(a[i]>=85) cnt2++;
    }
    double ans1=cnt1*1.0/(n*1.0);
    double ans2=cnt2*1.0/(n*1.0);
    cout<<(int)((ans1+0.005)*100)<<"%"<<endl;
    cout<<(int)((ans2+0.005)*100)<<"%"<<endl;
    return 0;
}

试题G:回文日期

题目
【问题描述】
2020年春节期间,有一个特殊的日期引起了大家的注意:2020年2月2日。因为如果将这个日期按“yyyymmdd”的格式写成一个8位数是20200202,恰好是一个回文数。我们称这样的日期是回文日期。有人表示20200202是“千年一遇”的特殊日子。对此小明很不认同,因为不到2年之后就是下一个回文日期:20211202即2021年12月2日。也有人表示20200202并不仅仅是一个回文日期,还是一个ABABBABA型的回文日期。对此小明也不认同,因为大约100年后就能遇到下一个ABABBABA型的回文日期:21211212即2121年12月12日。算不上“千年一遇”,顶多算“千年两遇”。给定一个8位数的日期,请你计算该日期之后下一个回文日期和下一个ABABBABA型的回文日期各是哪一天。
【输入格式】
输入包含一个八位整数N,表示日期。
【输出格式】
输出两行,每行1个八位数。第一行表示下一个回文日期,第二行表示下一个ABABBABA型的回文日期。
【样例输入】
20200202
【样例输出】
20211202
21211212
【评测用例规模与约定】
对于所有评测用例,10000101 <= N <= 89991231,保证N是一个合法日期的8位数表示。

分析
和D题一样,模拟日期,注意输出格式。

#include
using namespace std;
int a[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
}; //数组记录12个月的天数  a[0][m]对应平年,a[1][m]对应闰年
int leapyear(int y)//判断闰年
{
    return y%4==0&&y%100!=0||y%400==0;
}
int days(int y,int m)//返回y年m月的天数
{
    return a[leapyear(y)][m];
}
bool check1(int n) //回文
{
    int sum=0,k=n;
    while(n)
    {
        sum*=10;
        sum+=n%10;
        n/=10;
    }
    return sum==k;
}
bool check2(int n)//ababbaba回文
{
    int w[8],k=7;
    while(n)
    {
        w[k--]=n%10;
        n/=10;
    }
    if(w[0]!=w[2]||w[0]!=w[5]||w[0]!=w[7]||w[0]==w[1])
    return 0;
    if(w[1]!=w[3]||w[1]!=w[4]||w[1]!=w[6])
    return 0;
    return 1;
}
int main()
{
    int n,y,m,d;
    int y1,m1,d1,y2,m2,d2;
    bool flag1=1,flag2=1;
    cin>>n;
    y=n/10000,m=n/100%100,d=n%100;
    while(flag1||flag2)
    {
        //日期处理
        d++;
        if(d>days(y,m))
        {
            d=1;
            m++;
        }
        if(m>12)
        {
            m=1;
            y++;
        }
        n=y*10000+m*100+d;
        if(check1(n)&&flag1)
        {
            y1=y;m1=m;d1=d;
            flag1=0;
        }
        if(check2(n)&&flag2)
        {
            y2=y;m2=m;d2=d;
            flag2=0;
        }

    }
    printf("%04d%02d%02d\n",y1,m1,d1);
    printf("%04d%02d%02d\n",y2,m2,d2);
    return 0;
}

试题H:子串分值和

题目
2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)_第3张图片

2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)_第4张图片

分析
考试的时候看到样例就想到直接求子串,然后针对每个子串求f(s)值相加,超时。正确做法为遍历字符串每一个字符,记录当前字符在之前出现的位置,乘起来即为答案。
个人做法(超时):

#include
using namespace std;
map<string,int> m;
int f(string s)
{
    if(!m[s])//如果子串没有出现过
    {
        set<char>ss;
        for(int i=0;i<s.length();i++)
        ss.insert(s[i]);
        m[s]=ss.size();
    }
    return m[s];
    
}
int main()
{
    int i,j,ans=0;
    string s;
    cin>>s;
    for(i=0;i<s.length();i++)
    for(j=1;j<=s.length()-i;j++)
    {
        ans+=f(s.substr(i,j));
    }
    cout<<ans;
    return 0;
}

正解:对于字符串,我们记录每一个字符第一次出现的位置,例如字符串"ababc":第一个字符a出现位置为-1(初始化为),因此对于这个字符,前面可以选择的字符数量为1,后面可以选择的字符数量为n-i,两者相乘即为这个字符贡献的子串分值
2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)_第5张图片

#include
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int pre[maxn], last[maxn];
int main()
{
    ll ans = 0;
    string s;
    cin>>s;
    int n = s.size();
    for (int i = 0; i < 26;i++) //初始化为-1  方便计算
        last[i] = -1;
    for (int i = 0; i < n;i++)
    {
        int x = s[i] - 'a';
        pre[i] = last[x];
        ans += ll(i - pre[i]) * (n - i);
        last[x] = i;
    }
    // for (int i = 0; i < n;i++)  //测试pre[i]是否正确
    //     cout << pre[i] << " " << n-i << endl;
    cout << ans;
    return 0;
}

试题I:平面切分

题目
【问题描述】
平面上有N 条直线,其中第i条直线是y = Aix+Bi
请计算这些直线将平面分成了几个部分。
【输入格式】
输入的第一行包含一个整数N,
以下N行,包含两个证书 Ai,Bi
【输出格式】
一个整数代表答案
【样例输入】
31
1
2 2
3 3
【样例输出】
6

分析
直接放弃了,试着骗了下分

试题J:冒泡排序

题目
【问题描述】
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,
则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串lan 排序,只需要1 次交换。对于字符串qiao 排序,
总共需要4 次交换。
小蓝的幸运数字是V,他想找到一个只包含小写英文字母的字符串,对这
个串中的字符进行冒泡排序,正好需要V 次交换。请帮助小蓝找一个这样的字
符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,
请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
【输入格式】
输入一行包含一个整数V,为小蓝的幸运数字。
以下N行,包含两个证书 Ai,Bi
【输出格式】
输出一个字符串,为所求的答案。
【样例输入】
4
【样例输出】
bbaa

分析
当时想了很久,还是太菜了没做出来,用冒泡排序验证了一下,输出了n等于1~8的答案,骗了一下分

个人总结:

大三了第一次参加蓝桥杯(B组省赛),做完下来感受就是,还是以基础为重,注意细节,简单题不要做错!学一些stl库会非常方便。自己还是太菜了,只做对7道题左右,混了个省一,接下来国赛也没怎么准备,估计会白给。
2020第十一届蓝桥杯C++B组省赛第二场个人题解(部分)_第6张图片

你可能感兴趣的:(LANQIAO,算法,动态规划)