2024年第15届蓝桥杯C++大学C组代码

最近笔者在备战蓝桥杯时发现C++大学C组的题解没有完整的,一搜还是大片的大学B组,打完后,笔者打算自己出一篇题解个人思路。由于笔者是个蒟蒻,加之想蹭一波蓝桥杯的热度,在比赛完当天就赶出来这篇文章,所以肯定会有错误的,这篇文章也会持续更新到思路和代码全部正确为止,顺带一提,笔者暂时还没有学会用非常美观的LaTeX公式,后续学会了会重构一下这篇文章的,大佬轻喷。

另外欢迎各位大佬指出错误。

一、个人思路+代码
A.拼正方形
题意解析:

有7385137888721个2*2的正方形和10470245个1*1的正方形,问能拼出的最大正方形的边长。

个人思路:

都给面积了,为什么不把总面积算出来然后开二次根呢?

先把2*2的正方形数量乘以4得到2*2正方形的总面积,再加上1*1正方形的面积可以得出能拼出的总面积,再开二次根得到结果。

个人代码答案:5435123

B.劲舞团
题意解析:

给了2000次操作,每个操作一行,每行内第一个是待敲击字符(a),第二个是玩家(忘了名字)敲击的字符(b),第三个是当前的毫秒时间戳,a和b相等且和上一次的时间戳的差在1000以内成为1次连击,问这2000次操作内最大连击次数。

个人思路:

读到程序里,一行行遍历,如果前两个相等则看当前时间戳减上一行时间戳看是否满足1000以内,满足则tans++(临时存储),一旦不满足则让ans(最终答案)和tans取个大,然后tans=0,全部遍历完后输出ans看一下即可。

当然还有个既简单又愚蠢的办法,那就是把数据放到excel中,让第三个数据后减前得到当前操作和上次操作间隔的时间,然后一行行看,手动记录(doge),笔者自己就是这么做的(因为忘记了怎么读文件了,但凡它数据量达到5000以上,笔者绝对会灰溜溜地去翻文档)。

个人代码答案:9

代码之后更新。

C.数字诗意
题意解析:

一个数如果可以表示为连续的自然数之和(>=2个数字)称为诗意数字,问给n个数有几个不是诗意数字。

样例输入:

3
3 6 8
样例输出:

1
个人思路:

这个无需保存,即时读入即时处理即时记录。人话就是每读入一个数字就check一下是否是诗意数字,不是就ans++。判断的原理是奇数必定是诗意数字(1=0+1,3=1+2,5=2+3……),偶数如果是可以表示为2的n次幂(例如2,4,8,16)则不是诗意数字,其余偶数都是诗意数字(笔者从1到32中抽了几个有代表性的数字试出来的结论,所以笔者也不知道为什么是这样)。

个人代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];
bool check(int x)
{
    bool flag = false;
    if (x % 2) return false;
    else
    {
        while (x)
        {
            if (x % 2 && x != 1) flag = true;
            x /= 2;
        }
        if (flag) return false;
        return true;
    }
}
void solve()
{
    int ans = 0, n; cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        int x; cin >> x;
        if (check(x)) ans ++;
    }
    cout << ans;
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

D.封闭图形个数(排序)
题意解析:

8有两个封闭图形,0、4、6、9有一个封闭图形,1、2、3、5、7有一个封闭图形,按照一个数字的封闭图形个数为第一关键字,数字的大小为第二关键字排序,所以在本题中0到1从小到大排序的结果是1 2 3 5 7 0 4 6 9 8。

个人思路:

用结构体记录当前数字的大小和封闭图形数目,读入时用个函数算出来存到结构体中,重载一下<运算符sort一下就可以了。是不是很简单。

样例输入:

3
18 29 6
样例输出:

6 29 18
个人代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
struct A
{
    int x, y;
    bool operator < (const A & u) const
    {
        if (y == u.y) return x < u.x;
        return y < u.y;
    }
}a[N];
int ttt(int x)
{
    int res = 0;
    if (x == 0) return 1;
    while (x)
    {
        if (x % 10 == 8) res += 2;
        else if (x % 10 == 0 || x % 10 == 4 || x % 10 == 6 || x % 10 == 9)
            res ++;
        x /= 10;
    }
    return res;
}
void solve()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i].x, a[i].y = ttt(a[i].x);
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i ++ ) cout << a[i].x << ' ';
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

E.回文数组
题意解析:

给一个数组,每一步可以选择1个数字+1或-1,也可以选择相邻两个数字同时+1或-1,问要变成回文数组最少需要多少步。

个人思路:

两个不同数字如果要通过+1或-1的操作变成两个相同的数字,需要的次数是两个数字差的绝对值,于是把数组回文对应位置的差的绝对值存入另一数组,由于可以把两个相邻位置的压缩成那个大值(比如第一个数字需要5次+1操作变成最后一个数字,第二个数字需要3次+1操作变成倒数第二个数字,则可以前三次同时对第一个和第二个数字+1,这样第二个数字已经和倒数第二个数字相同,而第一个数字还差两次+1操作和最后一个数字相同,所以总共需要5次操作)。

上面一段是笔者想的比较清楚的地方,下边就是可能会有问题的地方了。

ans1记录每次找两个值,从第一个数字开始,每次加2,取i和i+1中的最大值。

ans2记录每次找两个值,从第二个数字开始,每次加2,取i-1和i中的最大值。

ans3记录每次找三个值,从第二个数字开始,每次加3,后边不大会描述,看代码吧。

样例输入:

4
1 2 3 4
样例输出:

3
个人代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int a[N], t[N], cnt;
void solve()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    int l = 1, r = n;
    while (l < r)
    {
        t[++ cnt] = abs(a[l] - a[r]);
        l ++, r --;
    }
    int ans1 = 0, ans2 = 0, ans3 = 0;
    for (int i = 1; i <= cnt; i +=2 ) ans1 += max(t[i], t[i + 1]);
    for (int i = 2; i <= cnt; i +=2 ) ans2 += max(t[i - 1], t[i]);
    for (int i = 2; i <= cnt; i +=3 )
    {
        int ma = max(max(a[i - 1], a[i]), a[i + 1]);
        int mi = min(min(a[i - 1], a[i]), a[i + 1]);
        ans3 += ma + mi;
    }
    cout << min(min(ans1, ans2), ans3);
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

F.商品库存管理(差分)
题意解析:

有n个商品,初始数目都为0,有m次操作,每次把l到r区间内的商品数目都+1,问如果不进行第i次操作会多几个数目为0的商品【好吧,写到这里,笔者发现自己好像读错题了,有可能是如果不进行第i次操作会有几个数目为0的商品(悲】。

个人思路:

先做个差分数组,用个结构体数组存每一次操作的l和r,同时进行差分,完事后对差分数组进行前缀和恢复成每个商品数目的数组。然后遍历结构体数组看l到r区间内有多少个1【好吧,就算没读错也会tle,不过能骗几分是几分。就在这里,笔者苦思冥想了很久都想不出如何优化,恳请各位大佬教教,给说说用什么算法优化也行】。

样例输入:

5 3
1 2
2 4
3 5
样例输出:

1
0
1
个人代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 10;
int a[N], diff[N];
struct OP
{
    int l, r;
}op[N];
void solve()
{
    int n, m; cin >> n >> m;
    for (int i = 1; i <= m; i ++ )
    {
        cin >> op[i].l >> op[i].r;
        diff[op[i].l] ++;
        diff[op[i].r + 1] --;
    }
    for (int i = 1; i <= n; i ++ ) a[i] = a[i - 1] + diff[i];
    for (int i = 1; i <= m; i ++ )
    {
        int ans = 0;
        for (int idx = op[i].l; idx <= op[i].r; idx ++ )
            if (a[idx] == 1) ans ++;
        cout << ans << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

G.挖矿
题意解析:

在一个数轴上,有n个点有矿坑,每个矿坑只能采集一个矿石(可以重复走但不重复给矿石),一共可以走m步,问最多可以采集到多少矿石。

个人思路:

笔者的思路仅到先排序然后判断只往正方向走m步能获得多少矿石或只往负方向走m步能获得多少矿石,再往后如果有走过来走过去的还没想明白,笔者自己想明白了后会补充上,当然,要有大佬指路就再好不过了。

样例输入:

5 4
0 -3 -1 1 2
样例输出:

4
个人代码:

所以暂时还没有,等想明白写出来了后再更新吧。

H.回文字符串
题意解析:

给定一个小写字符串s,在s前面可以加上任意数目的(l,q,b),问能否转化为回文串,如果可以输出“Yes”,否则输出“No”。

个人思路:

有三种情况,我们一一来介绍。

1:如果原本是回文字符串则可以。

2:如果该字符串中只存在l,q,b三种字符也可以。

3:如果去掉最后的l,q,b连着的子串,前面的子串是回文字符串也可以。

样例输入:

3
gmgqlq
palbll
aaa
样例输出:

Yes
No
Yes
个人代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int tt[27];
char s1[N];
bool check(string s)
{
    int l = 0, r = s.length() - 1;
    while (l < r)
    {
        if (s[l] != s[r]) return false;
        l ++, r --;
    }
    return true;
}
bool check1(int len)
{
    int l = 0, r = len;
    while (l < r)
    {
        if (s1[l] != s1[r]) return false;
        l ++, r --;
    }
    return true;
}
void solve()
{
    string s; cin >> s;
    for (int i = 0; i < s.length(); i ++ ) tt[s[i] - 'a'] ++;
    if (check(s) || tt['l' - 'a'] + tt['q' - 'a'] + tt['b' - 'a'] == s.length())
    {
        cout << "Yes\n";
        return;
    }
    int lqbl = s.length() - 1;
    for (; lqbl >= 0; lqbl -- )
        if (s[lqbl] != 'l' && s[lqbl] != 'q' && s[lqbl] != 'b') break;
    for (int i = lqbl; i >= 0; i -- ) s1[i] = s[i];
    if (check1(lqbl)) cout << "Yes\n";
    else cout << "No\n";
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
    return 0;
}

二、代码注释版
C.数字诗意
//万能头文件,涵盖了算竞用的大部分库
#include <bits/stdc++.h>
//十年oi一场空,不开long long见祖宗
#define int long long
//使用std命名空间
using namespace std;
const int N = 2e5 + 10;
int a[N];
//判断一下x是否是诗意数字
bool check(int x)
{
    bool flag = false;
    //奇数是诗意数字
    if (x % 2) return false;
    else
    {
        while (x)
        {
            //如果出现了非1的奇数说明该数不是2的n次幂,也就是诗意数字
            if (x % 2 && x != 1) flag = true;
            x /= 2;
        }
        if (flag) return false;
        return true;
    }
}
void solve()
{
    int ans = 0, n; cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        int x; cin >> x;
        //如果x是不是诗意数字,ans累加1
        if (check(x)) ans ++;
    }
    cout << ans;
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

D.封闭图形个数
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
//定义结构体
struct A
{
    //x指数字大小,y指当前数字封闭图形个数
    int x, y;
    //重载<运算符
    bool operator < (const A & u) const
    {
        //如果封闭图形个数相同,则比较数字大小
        if (y == u.y) return x < u.x;
        return y < u.y;
    }
}a[N];
//计算数字的封闭图形个数
int ttt(int x)
{
    int res = 0;
    //如果数字是0,直接返回1,因为0进不去while
    if (x == 0) return 1;
    //判断非0数字
    while (x)
    {
        //如果其中有8,加2,有0、4、6、9,加1
        if (x % 10 == 8) res += 2;
        else if (x % 10 == 0 || x % 10 == 4 || x % 10 == 6 || x % 10 == 9)
            res ++;
        //别忘了把最后一位去掉
        x /= 10;
    }
    return res;
}
void solve()
{
    int n; cin >> n;
    //读入并计算封闭图形数目
    for (int i = 1; i <= n; i ++ ) cin >> a[i].x, a[i].y = ttt(a[i].x);
    //排序
    sort(a + 1, a + 1 + n);
    //输出
    for (int i = 1; i <= n; i ++ ) cout << a[i].x << ' ';
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

E.回文数组
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
//t数组存放对应位数差的绝对值
int a[N], t[N], cnt;
void solve()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    //计算并存放对应位数差的绝对值
    int l = 1, r = n;
    while (l < r)
    {
        t[++ cnt] = abs(a[l] - a[r]);
        l ++, r --;
    }
    //计算ans1、ans2、ans3
    int ans1 = 0, ans2 = 0, ans3 = 0;
    //ans1从第一个开始找,我太蠢了,居然没注意到ans1和ans2算的是同一个玩意
    for (int i = 1; i <= cnt; i +=2 ) ans1 += max(t[i], t[i + 1]);
    for (int i = 2; i <= cnt; i +=2 ) ans2 += max(t[i - 1], t[i]);
    for (int i = 2; i <= cnt; i +=3 )
    {
        int ma = max(max(a[i - 1], a[i]), a[i + 1]);
        int mi = min(min(a[i - 1], a[i]), a[i + 1]);
        ans3 += ma + mi;
    }
    cout << min(min(ans1, ans2), ans3);
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

F.商品库存管理
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 10;
//a存放改完后的商品数目,diff表示差分数组
int a[N], diff[N];
//定义记录每次操作的结构体
struct OP
{
    int l, r;
}op[N];
void solve()
{
    int n, m; cin >> n >> m;
    //m次操作
    for (int i = 1; i <= m; i ++ )
    {
        cin >> op[i].l >> op[i].r;
        //差分
        diff[op[i].l] ++;
        diff[op[i].r + 1] --;
    }
    //前缀和求改完后的商品数目数组
    for (int i = 1; i <= n; i ++ ) a[i] = a[i - 1] + diff[i];
    for (int i = 1; i <= m; i ++ )
    {
        //如果该操作l到r之间存在商品数目为1的则记录上
        int ans = 0;
        for (int idx = op[i].l; idx <= op[i].r; idx ++ )
            if (a[idx] == 1) ans ++;
        cout << ans << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t --) solve();
    return 0;
}

H.回文字符串
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
//桶,tt记录每个字母出现了几次,下标是相对于字符a的位置
int tt[27];
//记录除去了尾部的l、q、b字符后的子串
char s1[N];
//检查一下原始字符串是否是回文字符串
bool check(string s)
{
    int l = 0, r = s.length() - 1;
    while (l < r)
    {
        //如果对应位置不相同就可以直接返回false了
        if (s[l] != s[r]) return false;
        l ++, r --;
    }
    return true;
}
//别问我为啥要写两份相似的check函数,因为写这题写一半忘了怎么写string插入了
bool check1(int len)
{
    int l = 0, r = len;
    while (l < r)
    {
        if (s1[l] != s1[r]) return false;
        l ++, r --;
    }
    return true;
}
void solve()
{
    string s; cin >> s;
    //遍历原始字符串,记录字母个数
    for (int i = 0; i < s.length(); i ++ ) tt[s[i] - 'a'] ++;
    //如果原始字符串是回文字符串或者l、q、b的字母数目和为原始串总长度,那就能变成回文字符串
    if (check(s) || tt['l' - 'a'] + tt['q' - 'a'] + tt['b' - 'a'] == s.length())
    {
        cout << "Yes\n";
        return;
    }
    //从后往前遍历,排除掉l、q、b
    //lqbl代表的是排除掉l、q、b之后的那个字符的下标
    int lqbl = s.length() - 1;
    for (; lqbl >= 0; lqbl -- )
        if (s[lqbl] != 'l' && s[lqbl] != 'q' && s[lqbl] != 'b') 
            break;
    //把前面的子串存到s1内
    for (int i = lqbl; i >= 0; i -- ) s1[i] = s[i];
    //检查一下s1是否是回文字串
    if (check1(lqbl)) cout << "Yes\n";
    else cout << "No\n";
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
    return 0;
}

三、参赛感想
总体感觉相较去年难度下来了,需要用到的算法不多(也许是需要用到的算法我不会),希望今年能进国赛吧,去年本蒟蒻只拿了省二(悲),说来奇怪,去年山东省大学C组省一比省二多,不知道今年什么情况呢。山东省内参加蓝桥杯的职业院校还是太少了,每所院校内参加的专科同学更是少之又少,说实话挺可惜的,明明稍微学学就能拿个省奖,却不参加,或者参加也不学,难道是都去准备专升本了?也对,难怪专升本录取分数线这么高,事实上,就我个人而言,学历只是一块敲门砖,如果不是有些公司必须要求本科学历,我是不会专升本的,技术才是王道。扯远了扯远了,拉回来。

讲下今天比赛时的状态吧,昨晚不知道为什么又失眠了,到凌晨2点才睡着,早上7点就醒了,又补了半个小时才稍微好点,提前到考点后吃了块面包,然后开始背一些算法板子,比如dfs、bfs、树状数组,结果都没用上(好吧,也许是我不会用)。

到了开始时,状态意外的好,一点不困,先花10分钟通读8道题,第一遍读的时候感觉A和B这都是啥,咋想呀,一看C题,一个check函数就能搞定,也想出来了奇数必定是诗意数字,偶数一部分是,一部分不是,然后先跳先看D,一看好家伙,这不是结构体排序吗?毁,我忘背怎么写自定义排序了,只能靠久远的记忆了,E一看回文就先跳,我最不擅长的就是回文一类了(当然还有很多不擅长的),F第一眼感觉需要用差分,先列入必做题,G第一遍不明白怎么做(虽然到最后也没明白就是了),H回文,跳。

回来做C和D,C手写演算那几个特殊的数字搞了10分钟,然后再写又是15分钟,D题首先是想写cmp函数的,结果没写对就改用结构体重载<了,然后幸亏把0到9都试了一遍,发现0没判断,然后赶紧加上了,虚惊一场。

又回来看来一眼B,一滑到底,嗯,2000行,不多,直接上excel吧,数了15分钟,算出来是9,然后再看一遍A,求边长,给2*2的和1*1的,可以不可以全部转化成1*1的?然后python大法,*4再加上1*1的,再sqrt一下,搞定。

此时我记得已经10点半了,然后把EFGH又看了一遍,最终还是决定先写E,E那个怎么绑定两个同时加想了好久,最终想出个凑合的做法。感觉FGH出不来的我就直接输出样例,看能不能给点分,然后仔细看F,发现没什么优化的好方法(我不会),就放弃了,打暴力吧,总比只输出样例强。G就先放放,又仔细看完H后总结出了那三条,开始搞,没想到搞出来了,这时已经12点半左右了,就记了下题和代码的主要部分。

12点出来后,不出意外的都在讨论B组题目,而我只能把B组题看一遍之后才能参与到他们的讨论中,我感到一阵的孤独,希望之后能取得个不错的成绩吧。中午吃饭时不知道为什么没有食欲,饿但吃不下去,有点反胃。

这篇文章从2点30开始写,晚上8点写完,之后会把它完善成题解的,出了成绩后也会把感想补充完整。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/m0_73099445/article/details/137715055

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值