第十一届蓝桥杯C++ A组省赛第二场题解(A至H题)

A题:门牌制作

题面

思路

        遍历1至2020,统计出现2的次数

答案:624

实现

#include <iostream>

using namespace std;

int main()
{
    int ans = 0;
    for(int i = 1; i <= 2020; i++)
        for(int j = i; j; j /= 10)
            if(j % 10 == 2) ans ++;

    cout << ans << endl;
    
    return 0;
}

B题:既约分数

题面

思路

        分子分母都遍历1至2020,判断它们的最大公约数

实现

#include <iostream>

using namespace std;

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

int main()
{
    int ans = 0;

    for(int i = 1; i <= 2020; i++)
        for(int j = 1; j <= 2020; j++)
            if(gcd(i, j) == 1) ans++;
    
    cout << ans << endl;

    return 0;
}

答案:2481215

C题:蛇形填数

题面

思路

        1.根据题目规律构建出矩阵

        2.用一个flag表示方向,向上走或者向下走

实现 

#include <iostream>

using namespace std;

const int I = 19, J = 19;
int a[100][100];

int main()
{
    int cnt = 1;
    int i = 0, j = 0;

    bool flag = true;
    while(i <= I || j <= J)
    {
        a[i][j] = cnt++;

        if(flag && i - 1 < 0) j++, flag = false; // 往上走, 并且走不了
        else if(!flag && j - 1 < 0) i++, flag = true;//往下走, 并且走不了
        else if(flag) i--, j++;//往上走
        else i++, j--;//往下走
    }

    for(int i = 0; i <= I; i++)
    {
        for(int j = 0; j <= J; j++)
            cout << a[i][j] << '\t';
        cout << endl;
    }

    cout << a[19][19] << endl;

    return 0;
}

答案:761 

D题:七段码

题面

思路

        七段码每一管只有亮灭两种,不考虑连通则有127种(除掉全灭)。所以用二进制枚举1到127,每一位的0或1代表管的亮或灭。

        用dfs遍历某一条亮的管,如果一次dfs没遍历完所有亮灯管,说明亮的管不连通,方案不计数

实现

#include <iostream>
#include <cstring>

using namespace std;

bool a[8][8];//a[i][j]表示i能到达j
bool st[8], light[8];

void dfs(int u)
{
    st[u] = true;
    for(int i = 1; i <= 7; i++)//如果没染过色,是亮的,而且有路径可以到达,就进行染色
        if(!st[i] && light[i] &&  a[u][i]) dfs(i);
}

int main()
{
    //下标1234567分别代表abcdefg
    a[1][2] = a[2][1] = a[1][6] = a[6][1] = true;
    a[2][3] = a[3][2] = a[2][7] = a[7][2] = true;
    a[3][4] = a[4][3] = a[3][7] = a[7][3] = true;
    a[4][5] = a[5][4] = true;
    a[5][6] = a[6][5] = a[5][7] = a[7][5] = true;
    a[6][7] = a[7][6] = true;

    int ans = 0;
    for(int i = 1; i <= 127; i++)
    {
        memset(st, 0, sizeof st);
        memset(light, 0, sizeof light);

        for(int k = 0; k < 7; k++)// 记录第几位是1
            if(i >> k & 1) light[k + 1] = true;
        
        // 找到第一个亮的管
        int j = 1;
        while(light[j] == false) j++;

        dfs(j);//染色

        //染色过后如果还有无法到达的亮灯管,则说明至少有两个连通块
        bool flag = true;
        for(int k = 1; k <= 7; k++) 
            if(light[k] && !st[k])
                flag = false;

        if(flag) ans++;
    }

    cout << ans << endl;

    return 0;
}

答案:80

E题:平面分割

题面

思路

        纯数学题,参考:2020年蓝桥杯---A组省赛E题---平面分割(数学)_雪岩的博客-CSDN博客_平面分割 蓝桥杯 

F题:成绩分析

题面

 

思路

        纯粹的语法题。printf在输出截断位小数时,会进行四舍五入,不需要特殊处理。

实现

#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 1e4 + 10;
int a[N];

int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);

    int minx = 110, maxx = -1;
    double sum = 0;
    for(int i = 0; i < n; i++)
    {
        minx = min(minx, a[i]);
        maxx= max(maxx, a[i]);
        sum += a[i];
    }

    printf("%d\n%d\n%.2f\n", maxx, minx, sum / n);

    return 0;
}

G题:回文日期

题面

思路 

        遍历10000101至89991231,找出合法的,并且是回文的日期。对于第二个答案,再从第一个答案找到的位置开始,判断是不是ABABBABA型

实现

#include <cstdio>

using namespace std;

char str[8];

//把数字转化成字符串,蓝桥杯练习系统不支持C++11,不能用to_string
void tostring(int n)
{
    for(int i = 7; i >= 0; i--)
    {
        str[i] = n % 10;
        n /= 10;
    }
}

bool pali(int n)//判断是否是回文
{
    tostring(n);
    for(int i = 0, j = 7; i <= j; i++, j--)
        if(str[i] != str[j]) return false;

    return true;
}

bool leap(int year)//闰年判断
{
    //能被四百整除或者,能被四整除且不能被一百整除
    if(year % 400 == 0) return true;
    if(year % 100 && year % 4 == 0) return true;

    return false;
}

bool legal(int n)//判断日期是否合法
{
    // 首先判定月份是否合法
    int month = (n / 1000 % 10) * 10 + n / 100 % 10;
    if(month == 0 || month > 12) return false;

    //日数判断
    int day = (n / 10 % 10) * 10 + n % 10;
    if(day == 0) return false;

    switch(month)
    {
        case 1 :
            if(day > 31) return false;
        case 2 :
            if(leap(n / 10000) && day > 29) return false;
            else if(day > 28) return false;
        case 3 :
            if(day > 31) return false;
        case 4 :
            if(day > 30) return false;
        case 5 :
            if(day > 31) return false;
        case 6 :
            if(day > 30) return false;
        case 7 :
            if(day > 31) return false;
        case 8 :
            if(day > 31) return false;
        case 9 :
            if(day > 30) return false;
        case 10:
            if(day > 31) return false;
        case 11:
            if(day > 30) return false;
        case 12:
            if(day > 31) return false;
    }

    return true;
}

bool abcase(int n)//判定ABABBABA型
{
    //这里的str不需要更新,pali  总是运行在abcase前面,总会优先把str更新
    char a = str[0], b = str[1];
    if(str[2] == a && str[5] == a && str[7] == a && str[3] == b && str[4] == b && str[6] == b)
        return true;
    
    return false;
}

int main()
{
    int n;
    scanf("%d", &n);

    do n++;
    while(!legal(n) || !pali(n));

    printf("%d\n", n);

    while(!legal(n) || !pali(n) || !abcase(n)) n++;

    printf("%d\n", n);

    return 0;
}

H题:子串分值

题面

思路

考虑对于字符串中的每一个字母,有多少个子串它能贡献一个1。

例如: @C@@@C@@C   @表示除C以外的字母

        设l,r分别为子串和右串能取到的下标

        对于第一个字母C,l可以取l = 1,2;r可以取 r  = 2,3,4,5

        所以有2 x 4 = 6个子串对于答案有贡献

        

       

        对于第二个字母C,l可以取l = 3,4,5,6;r可以取 r  = 6,7,8

        所以有4 x 3 = 12个子串对于答案有贡献        

        

        对于第三个字母C,l可以取l = 6,7,8;r可以取 r  = 8

        所以有3 x 1 = 3个子串对于答案有贡献

故只需要遍历字符串,计算每一个字符对答案的贡献值。

首先用一个栈数组存储每种字母出现的位置。计算每一个字符的同时,更新每种字母的pre值,对于下一个这种字母的下标,可以在栈中找到。

实现

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

const int N = 1e5 + 10, M = 26;
typedef long long LL;
int n, pre[M];//表示前面的字母位置
char str[N];
vector<stack<int> > vec(M);

int main()
{
    scanf("%s", str + 1);
    n = strlen(str + 1);

    //将每种字母出现的位置压入栈中,栈顶是每种字母最先出现的位置
    for(int i = n; i > 0; i--)
    {
        int ch = str[i] - 'a';
        vec[ch].push(i);
    }

    LL ans = 0;
    for(int i = 1; i <= n; i++)
    {
        int ch = str[i] - 'a';

        //计算左右两边有多少种可能性
        int r = n - i + 1, l = i - pre[ch];
        vec[ch].pop();//顶部记录的是当前i,不需要
        if(vec[ch].size()) r = vec[ch].top() - i;

        ans += l * r;
        pre[ch] = i;//保存当前i以备下一个相同的ch使用
    }

    cout << ans << endl;

    return 0;
}
//参考自:https://blog.csdn.net/LUSH_BOY/article/details/114606213?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~aggregatepage~first_rank_ecpm_v1~rank_v31_ecpm-3-114606213.pc_agg_new_rank&utm_term=%E8%93%9D%E6%A1%A5%E6%9D%AF%E5%AD%90%E4%B8%B2%E5%88%86%E5%80%BCC%2B%2B&spm=1000.2123.3001.4430

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值