本篇文章中的题解是我结合自己比赛中的思路或者所写的代码以及官方给的题解相结合, 总结出的一篇相对来说比较清晰的题解, 希望要备战蓝桥杯的小伙伴能够看到最后(每道题都会附上Java代码和C++代码, 放心食用)~
填空题
二进制位数
问题描述:
十进制整数 2 在十进制中是 1 位数,在二进制中对应 10 ,是 2 位数。
十进制整数 22 在十进制中是 2 位数,在二进制中对应 10110 ,是 5 位数。
请问十进制整数 2022 在二进制中是几位数?
思路: 在位运算中我们都知道, 二进制左移一位相当于乘2; 右移一位相当于除以2. 因此, 我们可以直接通过这个思路, 对2022每次除以2直至为0, 这就相当于是2022的二进制每次右移一位, 移到最后为0的时候, 就说明2022的二进制总共就会有这么多位.
C++代码:
#include <iostream>
using namespace std;
int main()
{
int n = 2022;
int res = 0;
while(n)
{
n /= 2;
res++;
}
cout << res;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static void main(String[] args){
int n = 2022;
int res = 0;
while(n > 0){
n /= 2;
res++;
}
System.out.println(res);
}
}
答案: 11
晨跑天数
问题描述:
小蓝每周六、周日都晨跑,每月的 1、11、21、31日也晨跑。其它时间不晨跑。
已知 2022年1月1日是周六,请问小蓝整个2022年晨跑多少天?
思路: 直接遍历2022年的所有日期, 判断该日期是否是 1、11、21、31日或者是周六和周日, 因为题目中已经说了2022年1月1日是周六, 所以我们可以开一个变量来进行计数, 每过一天就加1, 而初始值正好可以从一开始, 每次将这个值模7, 如果为1或者2时, 说明这一天是周六或者周日, 最后返回结果即可.
C++代码:
#include <iostream>
using namespace std;
const int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int cnt, res;
int main()
{
for(int i = 1; i <= 12; i++)
{
for(int j = 1; j <= days[i]; j++)
{
cnt++;
if(j == 1 || j == 11 || j == 21 || j == 31 || cnt % 7 == 1 || cnt % 7 == 2)
{
res++;
}
}
}
cout << res;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static int[] days = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public static void main(String[] args){
int cnt = 0;
int res = 0;
for(int i = 1; i <= 12; i++){
for(int j = 1; j <= days[i]; j++){
cnt++;
if(j == 1 || j == 11 || j == 21 || j == 31 || cnt % 7 == 1 || cnt % 7 == 2)
{
res++;
}
}
}
System.out.println(res);
}
}
答案: 138
调和级数
问题描述:
小蓝特别喜欢调和级数 S(n)=1/1+1/2+1/3+1/4+…+1/n 。
请问,n 至少为多大时,S(n)>12 ?
思路: 非常简单的一道送分题, 直接暴力枚举即可, 当大于12的时候跳出循环.
C++代码:
#include <iostream>
using namespace std;
const double sum = 12;
int n = 1;
double m;
int main()
{
while(1)
{
m += 1.0 / n;
if(m > sum) break;
n++;
}
cout << n;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static void main(String[] args){
int n = 1;
double sum = 12;
double m = 0;
while(true){
m += 1.0 / n;
if(m > sum){
break;
}
n++;
}
System.out.println(n);
}
}
答案: 91380
山谷
问题描述:
给定一个字母矩阵,如果矩阵中的某个位置不在四条边上,而且该位置上的字母小于其上下左右四个位置的字母,则称为一个山谷。
例如,对于如下矩阵
DDDDD
CADCE
FFFFA
共有两个山谷,位于第二行第二列和第四列。请注意第二行第三列和第三行第五列都不是山谷。
对于如下30行60列的字母矩阵(请用等宽字体查看),请问有多少个山谷?
PHQGHUMEAYLNLFDXFIRCVSCXGGBWKFNQDUXWFNFOZVSRTKJPREPGGXRPNRVY
STMWCYSYYCQPEVIKEFFMZNIMKKASVWSRENZKYCXFXTLSGYPSFADPOOEFXZBC
OEJUVPVABOYGPOEYLFPBNPLJVRVIPYAMYEHWQNQRQPMXUJJLOOVAOWUXWHMS
NCBXCOKSFZKVATXDKNLYJYHFIXJSWNKKUFNUXXZRZBMNMGQOOKETLYHNKOAU
GZQRCDDIUTEIOJWAYYZPVSCMPSAJLFVGUBFAAOVLZYLNTRKDCPWSRTESJWHD
IZCOBZCNFWLQIJTVDWVXHRCBLDVGYLWGBUSBMBORXTLHCSMPXOHGMGNKEUFD
XOTOGBGXPEYANFETCUKEPZSHKLJUGGGEKJDQZJENPEVQGXIEPJSRDZJAZUJL
LCHHBFQMKIMWZOBIWYBXDUUNFSKSRSRTEKMQDCYZJEEUHMSRQCOZIJIPFION
EEDDPSZRNAVYMMTATBDZQSOEMUVNPPPSUACBAZUXMHECTHLEGRPUNKDMBPPW
EQTGJOPARMOWZDQYOXYTJBBHAWDYDCPRJBXPHOOHPKWQYUHRQZHNBNFUVQNQ
QLRZJPXIOGVLIEXDZUZOSRKRUSVOJBRZMWZPOWKJILEFRAAMDIGPNPUUHGXP
QNJWJMWAXXMNSNHHLQQRZUDLTFZOTCJTNZXUGLSDSMZCNOCKVFAJFRMXOTHO
WKBJZWUCWLJFRIMPMYHCHZRIWKBARXBGFCBCEYHJUGIXWTBVTREHBBCPXIFB
XVFBCGKCFQCKCOTZGKUBMJRMBSZTSSHFROEFWSJRXJHGUZYUPZWWEIQURPIX
IQFLDUUVEOOWQCUDHNEFNJHAIMUCZFSKUIDUBURISWTBRECUYKABFCVKDZEZ
TOIDUKUHJZEFCZZZBFKQDPQZIKFOBUCDHTHXDJGKJELRLPAXAMCEROSWITDP
TPCCLIFKELJYTIHRCQAYBNEFXNXVGZEDYYHNGYCDRUDMPHMECKOTRWOSPOFG
HFOZQVLQFXWWKMFXDYYGMDCASZSGOVSODKJGHCWMBMXRMHUYFYQGAJQKCKLZ
NAYXQKQOYZWMYUBZAZCPKHKTKYDZIVCUYPURFMBISGEKYRGZVXDHPOAMVAFY
RARXSVKHTQDIHERSIGBHZJZUJXMMYSPNARAEWKEGJCCVHHRJVBJTSQDJOOTG
PKNFPFYCGFIEOWQRWWWPZSQMETOGEPSPXNVJIUPALYYNMKMNUVKLHSECDWRA
CGFMZKGIPDFODKJMJQWIQPUOQHIMVFVUZWYVIJGFULLKJDUHSJAFBTLKMFQR
MYJFJNHHSSQCTYDTEAMDCJBPRHTNEGYIWXGCJWLGRSMEAEARWTVJSJBAOIOJ
LWHYPNVRUIHOSWKIFYGTYDHACWYHSGEWZMTGONZLTJHGAUHNIHREQGJFWKJS
MTPJHAEFQZAAULDRCHJCCDYRFVVRIVUYEEGFIVDRCYGURQDREDAKUBNFGUPR
OQYLOBCWQXKZMAUSJGMHCMHGDNMPHNQKAMHURKTRFFACLVGRZKKLDACLLTEO
JOMONXRQYJZGINRNNZWACXXAEDRWUDXZRFUSEWJTBOXVYNFHKSTCENAUMNDD
XFDMVZCAUTDCCKXAAYDZSXTTOBBGQNGVVPJGOJOGLMKXGBFCPYPCKQCHBDDZ
WRXBZMQRLXVOBTWHXGINFGFRCCLMZNMJUGWWBSQFCIHUBSJOLLMSQSGHMCPH
ELSOTFLBGSFNPCUZSRUPCHYNVZHCPQUGRIWNIQXDFJPWPXFBLKPNPEELFJMT
思路: 这道题直接暴力枚举每一个位置(最外面一圈不需要枚举), 判断其上下左右四个位置是否都比自身高即可.
C++代码:
#include <iostream>
using namespace std;
char q[35][65];
int n, m;
int res;
int main()
{
for(int i = 0; i < 30; i++)
{
scanf("%s", &q[i]);
}
for(int i = 1; i < 29; i++)
{
for(int j = 1; j < 59; j++)
{
char ch = q[i][j];
if(ch < q[i - 1][j] && ch < q[i + 1][j] && ch < q[i][j - 1] && ch < q[i][j + 1]) res++;
}
}
cout << res;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
char[][] q = new char[30][60];
for(int i = 0; i < 30; i++){
q[i] = scanner.next().toCharArray();
}
int res = 0;
for(int i = 1; i < 29; i++){
for(int j = 1; j < 59; j++){
char ch = q[i][j];
if(ch < q[i - 1][j] && ch < q[i + 1][j] && ch < q[i][j - 1] && ch < q[i][j + 1]){
res++;
}
}
}
System.out.println(res);
}
}
答案: 276
矩阵拆分
问题描述:
小蓝有一个 100 行 100 列的矩阵,矩阵的左上角为 1。其它每个位置正好比其左边的数大 2,比其上边的数大 1 。
例如,第 1 行第 2 列为 3,第 2 行第 2 列 为 4,第 10 行第 20 列为 48。
小蓝想在矩阵中找到一个由连续的若干行、连续的若干列组成的子矩阵,使得其和为 2022,请问这个子矩阵中至少包含多少个元素(即子矩阵的行数和列数的乘积)。
思路: 对于这道题, 我们可以遍历每一个子矩阵的左上角坐标和右下角坐标, 然后将这个子矩阵中的所有值都加起来, 看是否等于2022, 如果等于则更新子矩阵中包含的最少元素. 值得注意的是, 我们如果单纯对子矩阵进行遍历求值的话, 那么势必还需要再套上两层循环, 所以对于求矩阵的区间和有一种非常经典的求法 — 前缀和. 使用前缀和的做法在计算子矩阵的总和的时候, 就只需要进行加减法的操作(时间复杂度为O(1)), 不再需要遍历子矩阵求和.
C++代码:
#include <iostream>
using namespace std;
const int N = 110;
int a[N][N], s[N][N];
int res = 10000;
int main()
{
a[1][1] = 1;
//获取原数组的第一行
for(int i = 2; i <= 100; i++)
a[1][i] = a[1][i - 1] + 2;
//获取原数组矩阵
for(int i = 2; i <= 100; i++)
for(int j = 1; j <= 100; j++)
a[i][j] = a[i - 1][j] + 1;
//计算前缀和矩阵
for(int i = 1; i <= 100; i++)
for(int j = 1; j <= 100; j++)
s[i][j] = s[i - 1][j] + s[i][j - 1] + a[i][j] - s[i - 1][j - 1];
//枚举所有子矩阵
for(int i = 1; i <= 100; i++)
for(int j = 1; j <= 100; j++)
for(int k = i; k <= 100; k++)
for(int p = j; p <= 100; p++)
{
long sum = s[k][p] - s[k][j - 1] - s[i - 1][p] + s[i - 1][j - 1];
if(sum == 2022)
{
int cnt = (k - i + 1) * (p - j + 1);
res = min(res, cnt);
}
}
cout << res;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static void main(String[] args){
int res = 10000;
int[][] a = new int[110][110];
int[][] s = new int[110][110];
a[1][1] = 1;
//获取原数组的第一行
for(int i = 2; i <= 100; i++){
a[1][i] = a[1][i - 1] + 2;
}
//获取原数组矩阵
for(int i = 2; i <= 100; i++){
for(int j = 1; j <= 100; j++){
a[i][j] = a[i - 1][j] + 1;
}
}
//计算前缀和矩阵
for(int i = 1; i <= 100; i++){
for(int j = 1; j <= 100; j++){
s[i][j] = s[i - 1][j] + s[i][j - 1] + a[i][j] - s[i - 1][j - 1];
}
}
//枚举所有子矩阵
for(int i = 1; i <= 100; i++){
for(int j = 1; j <= 100; j++){
for(int k = i; k <= 100; k++){
for(int p = j; p <= 100; p++){
long sum = s[k][p] - s[k][j - 1] - s[i - 1][p] + s[i - 1][j - 1];
if(sum == 2022){
int cnt = (k - i + 1) * (p - j + 1);
res = Math.min(res, cnt);
}
}
}
}
}
System.out.println(res);
}
}
答案: 12
编程题
做核酸
问题描述:
如果周一做核酸,周二显示核酸天数为 1 天,周三显示 2 天,以此类推,周六显示 5 天,周日显示 6 天。
小蓝在某一天做了一次核酸,请问他的核酸显示为几天。已知做核酸和查看核酸不是在同一天,而且相差不超过 6 天(显示的数为 1 到 6 之间的数)。
输入格式:
输入第一行包含一个整数 s ,表示小蓝做核酸是周几。 s 为 1 到 6 依次表示周一到周六,s 为 7 表示周日。
第二行包含一个整数 t ,表示查看核酸是周几。 t 为 1 到 6 依次表示周一到周六,t 为 7 表示周日。输出格式:
输出一行包含一个整数,表示答案。
样例输入
5
2
样例输出
4
评测用例规模与约定: 对于所有评测用例,1 <= s, t <= 7。
思路: 这道也是送分题, 直接计算差值即可(t - s), 由于这个值可能会小于0(跨越一个星期), 所以我们经常会使用的写法是先 + 7 再 % 7 即可解决.
C++代码:
#include <iostream>
using namespace std;
int s, t;
int main()
{
cin >> s >> t;
cout << (t - s + 7) % 7 << endl;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int s = scanner.nextInt();
int t = scanner.nextInt();
System.out.println((t - s + 7) % 7);
}
}
字母转换
问题描述:
输入一个由小写英文字母组成的字符串,请将其中的元音字母(a, e, i, o, u)转换成大写,其它字母仍然保持小写。
输入格式
输入一行包含一个字符串。
输出格式
输出转换后的字符串。
样例输入
lanqiao
样例输出
lAnqIAO
评测用例规模与约定: 对于所有评测用例,字符串的长度不超过100。
思路: 相信每一个初学者都做过这样的题了, 这里就不详细讲了, 直接小写变大写即可.
C++代码:
#include <iostream>
#include <cstring>
using namespace std;
string s;
int main()
{
cin >> s;
for(int i = 0; i < s.size(); i++)
if(s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u')
s[i] -= 32;
cout << s << endl;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
char[] q = scanner.next().toCharArray();
for(int i = 0; i < q.length; i++){
char x = q[i];
if(x == 'a' || x == 'e' || x == 'i' || x == 'o' || x == 'u'){
q[i] -= 32;
}
}
System.out.println(new String(q));
}
}
充电能量
问题描述:
小蓝有一个充电器,可以使用不同的电压和电流充电。给定充电器工作的记录,请计算在这个记录期间总共通过充电传输了多少电能。
输入格式
输入第一行包含一个整数 n , 表示记录的条数。
接下来 n 行,每行包含一个时刻 T 和两个非负整数 U, I,表示在时刻 T 充电电压变为 U(单位伏),电流变为 I(单位A)。最后一行满足 U 和 I 均为 0,在前面的行中也可能出现 U、I 为 0 的情况。其中时间表示为 HH:MM:SS 的格式,时分秒分别用两位十进制数表示(补前导零)。
输入保证时刻依次递增且在 00:00:00 至 23:59:59 的区间内,不用考虑跨过零点充电的情况。
输出格式
输出一个整数,表示总共通电的电能为多少焦耳,其中 1 焦耳等于 1 伏乘以1 安乘以 1 秒。
样例输入
3
12:00:00 12 1
12:01:02 5 2
12:01:10 0 0
样例输出
824
思路: 我们可以开一个数组(表示在下标i的时间时, 电能的修改值), 下标代表秒数, 由于24 * 3600 = 86400, 所以我们开个大小大于86400的数组即可. 将所有电能都存到这个数组对应的位置上, 最后计算总电能就是两相邻的时间间隔乘上前时间位置上的电能即可.
C++代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100000;
int q[N];
int n, U, I, h, m, t, i, j;
string s;
long long res;
int getSecond(int h, int m, int s)
{
return h * 3600 + m * 60 + s;
}
int main()
{
cin >> n;
for(int i = 0; i < N; i++) q[i] = -1;
while(n--)
{
cin >> s >> U >> I;
h = stoi(s.substr(0, 2));
m = stoi(s.substr(3, 5));
t = stoi(s.substr(6, 8));
int second = getSecond(h, m, t);
q[second] = U * I;
}
for(i = 0; i < N; i++)
if(q[i] != -1) break;
while(i < N)
{
for(j = i + 1; j < N; j++)
{
if(q[j] != -1)
{
res += (j - i) * q[i];
i = j;
}
}
if(j == N) break;
}
cout << res << endl;
return 0;
}
Java代码:
import java.util.*;
public class Main{
public static int getSecond(int h, int m, int t){
return h * 3600 + m * 60 + t;
}
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] q = new int[100000];
for(int i = 0; i < 100000; i++){
q[i] = -1;
}
while(n-- > 0){
String s = scanner.next();
int U = scanner.nextInt();
int I = scanner.nextInt();
int h = Integer.parseInt(s.substring(0, 2));
int m = Integer.parseInt(s.substring(3, 5));
int t = Integer.parseInt(s.substring(6, 8));
int second = getSecond(h, m, t);
q[second] = U * I;
}
int i = 0;
int j = 0;
for(i = 0; i < 100000; i++){
if(q[i] != -1){
break;
}
}
long res = 0;
while(i < 100000){
for(j = i + 1; j < 100000; j++){
if(q[j] != -1){
res += (j - i) * q[i];
i = j;
}
}
if(j == 100000){
break;
}
}
System.out.println(res);
}
}
全相等三角形
问题描述:
给定一个字母矩阵,定义一个LQ三角形为某行中连续的几个字母、某列中连续的几个字母和一条45度的斜线中连续的几个字母组成的等腰直角三角形的边缘部分,其中每条边上的字母数量相等且至少为2 。
例如,对于下面的字母矩阵中,所有的字母 L 组成一个LQ三角形,所有字母 Q 组成了一个 LQ 三角形,所有字母 C 也组成了一个 LQ 三角形。
AAAAAAA
ALLLLLA
ALQQLAA
ALQLAAC
ALLAACC
ALAACCC
如果一个 LQ 三角形边上的所有字母相等,则称为一个全相等三角形。以三个例子都是全相等三角形。
给定一个字母矩阵,请求其中有多少个全相等三角形。
输入格式
输入第一行包含两个整数 n, m,分别表示字母矩阵的行数和列数。
接下来 n 行,每行 m 个大写字母,为给定的矩阵。
输出格式
输出一行,包含一个整数,表示答案。
样例输入
3 4
AAAA
ALAQ
ALQQ
样例输出
4
样例输入
6 7
AAAAAAA
ALLLLLA
ALQQLAA
ALQLAAC
ALLAACC
ALAACCC
样例输出
23
评测用例规模与约定
对于 50% 的评测用例,1 <= n, m <= 10。
对于所有评测用例,1 <= n, m <= 100。
C++代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int a[N][N], r[N][N][26], c[N][N][26], d1[N][N][26], d2[N][N][26];
int n, m, res;
int get1(int x, int y, int len)
{
return r[x][y][a[x][y]] - r[x - len][y][a[x][y]];
}
int get2(int x, int y, int len)
{
return c[x][y][a[x][y]] - c[x][y - len][a[x][y]];
}
int get3(int x, int y, int len)
{
return d1[x][y][a[x][y]] - d1[x - len][y + len][a[x][y]];
}
int get4(int x, int y, int len)
{
return d2[x][y][a[x][y]] - d2[x - len][y - len][a[x][y]];
}
bool check1(int x, int y, int len)
{
if(x - len + 1 < 1 || y + len - 1 > m) return false;
if(get1(x, y, len) == len && get2(x, y + len - 1, len) == len && get4(x, y + len - 1, len) == len) return true;
return false;
}
bool check2(int x, int y, int len)
{
if(x - len + 1 < 1 || y - len + 1 < 1) return false;
if(get1(x, y, len) == len && get2(x, y, len) == len && get3(x, y - len + 1, len) == len) return true;
return false;
}
bool check3(int x, int y, int len)
{
if(x + len - 1 > n || y - len + 1 < 1) return false;
if(get1(x + len - 1, y, len) == len && get2(x, y, len) == len && get4(x + len - 1, y, len) == len) return true;
return false;
}
bool check4(int x, int y, int len)
{
if(x + len - 1 > n || y + len - 1 > m) return false;
if(get1(x + len - 1, y, len) == len && get2(x, y + len - 1, len) == len && get3(x + len - 1, y, len) == len) return true;
return false;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
char ch;
cin >> ch;
a[i][j] = ch - 'A';
for(int k = 0; k < 26; k++)
c[i][j][k] = c[i][j - 1][k];
c[i][j][a[i][j]]++;
}
for(int j = 1; j <= m; j++)
for(int i = 1; i <= n; i++)
{
for(int k = 0; k < 26; k++)
r[i][j][k] = r[i - 1][j][k];
r[i][j][a[i][j]]++;
}
for(int val = 2; val <= n + m; val++)
for(int i = 1; i <= n; i++)
{
int j = val - i;
if(j < 1 || j > m) continue;
for(int k = 0; k < 26; k++)
d1[i][j][k] = d1[i - 1][j + 1][k];
d1[i][j][a[i][j]]++;
}
for(int val = 0; val < n + m; val++)
for(int i = 1; i <= n; i++)
{
int j = m - val + i;
if(j < 1 || j > m) continue;
for(int k = 0; k < 26; k++)
d2[i][j][k] = d2[i - 1][j - 1][k];
d2[i][j][a[i][j]]++;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int len = 2; len <= min(n, m); len++)
res += check1(i, j, len) + check2(i, j, len) + check3(i, j, len) + check4(i, j, len);
cout << res << endl;
return 0;
}
最后一题需要使用最小表示法的算法, 由于本人还没学(先搁着, 后面一定会补充~), 倒数第二题(也就是上面全相等三角形)官方使用的方法是前缀和的思想, 由于本人还没完全get到这个思想, 官方的代码先附上, 后面会在更新最后一题的时候补充上思路~