第十一届蓝桥杯c++b组

文章列举了多个蓝桥杯编程竞赛中的题目,包括既约分数、蛇形填数、跑步锻炼、成绩统计、子串分值和、平面切分以及回文日期等。针对每个题目,作者分析了错误代码的问题,提供了修正后的AC代码,并分享了解题思路和优化方法,如使用最大公约数函数、动态规划、模拟日期计算等。
摘要由CSDN通过智能技术生成

1.既约分数

P1509 - [蓝桥杯2020初赛] 既约分数 - New Online Judge

错误代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}
int main()
{
    LL res = 0;
    for (int i = 1; i <=2020; i++) {
        for (int j = i+1; j <= 2020; j++) {
            if (gcd(i, j) == 1) res++;
        }
    }
    cout << res*2<< endl;
    return 0;
}

思路:写一个最大公约数函数,然后枚举使得分子小于分母的情况,如果满足最大公约数为1,就计数加1,然后再乘2(分母比分子大的情况) 

但是忘掉了一种情况,分子等于分母,只有(1,1) 

这样,答案少了1,但是对于结果填空题来说,就是错的

AC代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}
int main()
{
    LL res = 0;
    for (int i = 1; i <=2020; i++) {
        for (int j = i+1; j <= 2020; j++) {
            if (gcd(i, j) == 1) res++;
        }
    }
    cout << res*2+1<< endl;
    return 0;
}

 2.蛇形填数

P1510 - [蓝桥杯2020初赛] 蛇形填数 - New Online Judge

找规律,进行模拟

从第一个数开始,发现每次都是四个过程

1.j++

2.i++,j--一直到j等于1

3.i++

4.i--,j++一直到i等于1

错误代码:

错误原因:数组开小了,因为a[30][30]就够用了,实际上有可能算到a[20][20]的过程中某一可能要超过30

注意:要在每个过程中判断i和j是否同时等于20,否则如果在while循环后面判断的话,i等于1或者j等于1了 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
LL a[30][30];
int main()
{
    LL cnt = 1;
    a[1][1] = 1;
    int i = 1, j = 1;
    while (1) {
        j++;
        a[i][j] = ++cnt;
        if (i == 20 && j == 20) {
            cout << cnt << endl;
            return 0;
        }
        while (j != 1) {
            i++;
            j--;
            a[i][j] = ++cnt;
            if (i == 20 && j == 20) {
                cout << cnt << endl;
                return 0;
            }
        }
        i++;
        a[i][j] = ++cnt;
        if (i == 20 && j == 20) {
            cout << cnt << endl;
            return 0;
        }
        while (i != 1) {
            i--;
            j++;
            a[i][j] = ++cnt;
            if (i == 20 && j == 20) {
                cout << cnt << endl;
                return 0;
            }
        }
    }
    return 0;
}

修改代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
LL a[100][100];
int main()
{
    LL cnt = 1;
    a[1][1] = 1;
    int i = 1, j = 1;
    while (1) {
        j++;
        a[i][j] = ++cnt;
        if (i == 20 && j == 20) {
            cout << cnt << endl;
            return 0;
        }
        while (j != 1) {
            i++;
            j--;
            a[i][j] = ++cnt;
            if (i == 20 && j == 20) {
                cout << cnt << endl;
                return 0;
            }
        }
        i++;
        a[i][j] = ++cnt;
        if (i == 20 && j == 20) {
            cout << cnt << endl;
            return 0;
        }
        while (i != 1) {
            i--;
            j++;
            a[i][j] = ++cnt;
            if (i == 20 && j == 20) {
                cout << cnt << endl;
                return 0;
            }
        }
    }
    return 0;
}

发现根本用不着数组,只要枚举i,j就行了

再次修改代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int main()
{
    LL cnt = 1;
    int i = 1, j = 1;
    while (1) {
        j++;
        cnt++;
        if (i == 20 && j == 20) {
            cout << cnt << endl;
            return 0;
        }
        while (j != 1) {
            i++;
            j--;
            cnt++;
            if (i == 20 && j == 20) {
                cout << cnt << endl;
                return 0;
            }
        }
        i++;
        cnt++;
        if (i == 20 && j == 20) {
            cout << cnt << endl;
            return 0;
        }
        while (i != 1) {
            i--;
            j++;
            cnt++;
            if (i == 20 && j == 20) {
                cout << cnt << endl;
                return 0;
            }
        }
    }
    return 0;
}

3.跑步锻炼 

P1513 - [蓝桥杯2020初赛] 跑步锻炼 - New Online Judge

模拟,枚举年月日

2000年到2020年

对于每年,从1月到12月,

对于每个月,情况不同,从1号到28号或29号或或31号,弄个数组,记录每月的天数30号

日子一天一天增加,星期就随着日子一天一天增加

星期的变化就从1到7,如果到7,就变为0(不要用约瑟夫循环%7,这样反而容易弄错)

 注意,闰年的2月是29天,平年的闰月是28天

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int month[13] = { 0,31,29,31,30,31,30,31,31,30,31,30,31 };
int month1[13] = {0, 31,28,31,30,31,30,31,31,30,31,30,31 };
int main()
{
    int weekday=6;
    LL res = 0;
    for (int i = 2000; i <= 2020; i++) {
        if ((i % 4 == 0&&i%100!=0)||i%400==0) {
            for (int j = 1; j <=12; j++) {
                for (int k = 1; k <= month[j]; k++) {
                    if (weekday == 1 || k == 1) res += 2;
                    else res += 1;
                    if (i == 2020 && j == 10 && k == 1&&weekday==4) {
                        cout << res << endl;
                        return 0;
                    }
                    if (weekday == 7) weekday = 0;
                    weekday++;
                }
            }
        }
        else {
            for (int j = 1; j <=12; j++) {
                for (int k = 1; k <= month1[j]; k++) {
                    if (weekday == 1 || k == 1) res += 2;
                    else res += 1;
                    if (weekday == 7) weekday = 0;
                    weekday++;
                }
            }
        }
    }
    return 0;
}

4.成绩统计 

 P1522 - [蓝桥杯2020初赛] 成绩统计 - New Online Judge

四舍五入:

保留一位小数:比如说对71.4四舍五入,只要加上0.5,再取整就可以了

保留两位小数:比如说对71.45四舍五入,只要先使71.45*10=714.5,再加上0.5,再取整,最后/10

保留三位小数:比如说对71.456四舍五入,只要先使71.456*100=7145.6,再加上0.5,再取整,最后/100

总之,先乘10的幂使之只有一位小数,然后加上0.5,取整,最后再除以10的幂

注意:在算的过程中要注意数据类型的转换,需要在中途强制类型转换

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 100010;
int a[N];
int n;
int cnt, cnt1;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        if (a[i] >= 60 ) cnt++;
        if (a[i] >= 85) cnt1++;
    }
    int x = (double)cnt / n*100+0.5;
    int y = (double)cnt1 / n*100+0.5;
    printf("%d%%\n%d%%\n", x, y);
    return 0;
}

5.子串分值和 

P1523 - [蓝桥杯2020初赛] 子串分值和 - New Online Judge

代码(过50%样例)

用i,j来枚举连续子串(i代表头,j代表尾),对于某一子串,k从i到j,来判断子串有多少个不同的字符,用桶计数的思想,如果该字符的个数是0,那么不同字符个数计数加1

记得每次枚举完一个子串,要初始化桶

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<string.h>
using namespace std;
typedef long long LL;
const int N = 100010;
char s[N];
int cnt[N];
int main()
{
    cin >> s;
    LL res = 0;
    int n = strlen(s);
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            for (int k = i; k <= j; k++) {
                if (cnt[s[k]] == 0) res++;
                cnt[s[k]]++;
            }
            memset(cnt, 0, sizeof cnt);
        }
    }
    cout << res << endl;
        return 0;
}

发现数组下标为字符也能运行并且通过样例,应该是字符本身存储的就是整数,所以没关系

不过,可以把cnt[s[k]]改为cnt[s[k]-'a']

优化:对于每个字符,来算它的贡献度,然后把所有贡献度加起来

每个字符只有在第一次出现时才有贡献度,统计每个字符在第一次出现时被多少子串包含

用last[s[i]]记录字符s[i]上一次出现的位置,那么如何算该字符第一次出现时被多少子串所包含呢?将该字符往左数,一直数到相同字符的前一个的个数将该字符往右数,一直数到最后一个字符的个数

这样就分成了两段,左边一段是包含该字符的子段,右边一段也是包含该字符的子段,两子段的个数相乘就是包含该字符的总子段个数,也就是该字符的贡献度(可以找一个样例来试一下,发现是这样的)

注意,在运算的过程中可能会爆int,因此需要强制类型转换成long long

AC代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 100010;
int last[N];
int main()
{
    string s;
    cin >> s;
    int n = s.size();
    LL res = 0;
    s = ' ' + s;
    for (int i = 1; i <= n; i++) {
        res += (LL)(i - last[s[i]]) * (n - i + 1);
        last[s[i]] = i;
    }
    cout << res << endl;
    return 0;
}

6.平面切分 

P1524 - [蓝桥杯2020初赛] 平面切分 - New Online Judge

思路:

一条直线如果与之前的直线没有交点,那么增加一个部分

如果与之前的直线有交点,那么在增加一个部分的基础上,产生了几个交点(可能与这条直线的交点和那条直线的交点一样,这样算是一个交点,因此需要去重,用set容器),就再增加几个部分

如果与之前的某一条直线重合了的话,那么由于之前已经算过了,所以就不再增加新的部分了

具体操作:用结构体储存斜率和截距,然后枚举每一条直线,对于每一条直线,如果和之前的某一条直线重复的话,就break,

并标记为false,如果平行的话,就continue,如果有交点的话。就算出交点坐标,并将坐标放入set容器里,最后加上set容器里坐标个数再加1来算出枚举的该条直线增加了几部分

将所有的直线增加的部分加起来,再加上一开始的一个部分,就是总共的部分

set容器:set就相当于一个集合,里面的元素是有序的且独一无二的

头文件:#include

set变量的定义:set<int>s;

操作:

 

 注意定义set变量的位置,放在i循环的下面,即对于每一条直线,都重新弄一个set容器,来存储产生的交点个数

#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;
typedef pair<double, double>PII;
const int N = 1010;
int n;
struct Line {
    double k, b;
}l[N];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> l[i].k >> l[i].b;
    }
    LL res = 1;
    for (int i = 1; i <= n; i++) {
        set<PII>q;
        bool flag = true;
        for (int j = 1; j <= i - 1; j++) {
            if (l[i].k == l[j].k ) {
                if (l[i].b == l[j].b) {
                    flag = false;
                    break;
                }
                else continue;
            }
            PII t;
            t.first = (l[i].b - l[j].b) / (l[j].k - l[i].k);
            t.second = l[i].k * t.first + l[i].b;
            q.insert(t);
        }
        if (flag) res += q.size() + 1;
    }
    cout << res << endl;
    return 0;
}

7.回文日期

P1518 - [蓝桥杯2020初赛] 回文日期 - New Online Judge

对于每一年,它的回文日期就确定了,只要判断日期合不合法就行了

先将年份的四位数分别取出,利用取十进制的方法算出回文的月和日,判断是否合法

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int m1[13] = { 0,31,29,31,30,31,30,31,31,30,31,30,31 };
int m2[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int ans,ans1,ans2;
int main()
{
    int t;
    cin >> t;
    while (t--) {
        int nowday;
        cin >> nowday;
        int now = nowday / 10000;
        bool flag1 = false, flag2 = false;
        for (int i = now ; i <= 9999; i++) {
            int n = i;
            int a = n % 10;
            int b = n / 10 % 10;
            int c = n / 100 % 10;
            int d = n / 1000;
            int month = a * 10 + b;
            int day = c * 10 + d;
            int ans = n * 10000 + month*100 +day;
            if (ans <= nowday) continue;
            if (n % 4 == 0 && n % 100 != 0 || n % 400 == 0) {
                if (month >= 1 && month <= 12 && day >= 1 && day <= m1[month]) {
                    if (!flag1) {
                        ans1 = ans;
                        flag1 = true;
                    }
                    if (a == c && b == d) {
                        ans2 = ans;
                        flag2 = true;
                    }
                }
            }
            else {
                if (month >= 1 && month <= 12 && day >= 1 && day <= m2[month]) {
                    if (!flag1) {
                        ans1 = ans;
                        flag1 = true;
                    }
                    if (a == c && b == d) {
                        ans2 = ans;
                        flag2 = true;
                    }
                }
            }
            if (flag1 && flag2) break;
        }
        cout << ans1 << endl;
        cout << ans2 << endl;
    }
    return 0;
}

8.七段码 

P1511 - [蓝桥杯2020初赛] 七段码 - New Online Judge 

在csdn上看了大佬(业余算法学徒)的文章,用了两种方法 

法一:将所有情况打印出来

由于一共只有七段,这刚好符合二进制的表示,二进制的表示一共是7位(符合七段),每一位用0和1表示(符合每一段的亮与灭)

只要用七位分别表示七段的图案就行了

二进制表示范围为0到127

x>>i&1表示二进制表示中第i位是多少(最低位是第0位)

具体见位运算
(6条消息) 算法:位运算_m0_74087709的博客-CSDN博客icon-default.png?t=N2N8https://blog.csdn.net/m0_74087709/article/details/128595662

数一下:一共有127种,有47种不联通,那么有80种联通 

#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
    int cnt = 1;
    for (int x = 1; x <= 127; x++)
    {
        cout << "====" << cnt++ << "====" << endl;
        for (int i = 0; i < 7; i++)
        {
            if (i == 0)
            {
                if (x >> i & 1) cout << " --";
                cout << endl;
            }
            if (i == 1)
            {
                if (x >> i & 1) cout << '|';
                else cout << " ";
            }
            if (i == 2)
            {
                if (x >> i & 1) cout << "  |";
                cout << endl;
            }
            if (i == 3)
            {
                if (x >> i & 1) cout << " --";
                cout << endl;
            }
            if (i == 4)
            {
                if (x >> i & 1) cout << '|';
                else cout << " ";
            }
            if (i == 5)
            {
                if (x >> i & 1) cout << "  |";
                cout << endl;
            }
            if (i == 6)
            {
                if (x >> i & 1) cout << " --";
                cout << endl;
            }
        }
        cout << endl;
    }

    return 0;
}

法二:dfs&&并查集

用1到7表示a到g,用邻接矩阵在相邻的两段连上无向边

用dfs枚举所有的亮灭情况(类似于(7条消息) atcoder ABC 128_m0_74087709的博客-CSDN博客),用并查集判断是否只有一个联通块

连边的时候,对于每一段去连它的边,这样能做到不重不漏 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 10;
int e[N][N];
int st[N];
int p[N];
int res;
int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void dfs(int x) {
    if (x <= 7) {
        st[x] = 1;
        dfs(x + 1);
        st[x] = 0;
        dfs(x + 1);
    }
    else {
        for (int i = 1; i <= 7; i++) p[i] = i;
        for (int i = 1; i <= 7; i++) {
            for (int j = 1; j <= 7; j++) {
                if (e[i][j] && st[i] && st[j]) {
                    p[find(i)] = find(j);
                }
            }
        }
        int cnt = 0;
        for (int i = 1; i <=7; i++) {
            if (st[i]&&p[i] == i) cnt++;
        }
        if (cnt == 1) res += 1;
        return;
    }
}
int main()
{
    e[1][2] = e[1][6] = 1;
    e[2][1] = e[2][3] = e[2][7] = 1;
    e[3][2] = e[3][4] = e[3][7] = 1;
    e[4][3] = e[4][5] = 1;
    e[5][4] = e[5][6] = e[5][7] = 1;
    e[6][5] = e[6][1] = e[6][7] = 1;
    e[7][2] = e[7][3] = e[7][5] = e[7][6] = 1;
    dfs(1);
    cout << res << endl;
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值