赞
踩
最近笔者在备战蓝桥杯时发现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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。