第十四届蓝桥杯第一期模拟赛题解[官方模拟赛]

19 篇文章 2 订阅
15 篇文章 1 订阅

第十四届蓝桥杯第一期模拟赛题解

蓝桥杯官网模拟赛排名

第一次模拟赛排名截图

A题-二进制位数(填空题)

题面

输出2022的二进制表示的位数。

算法(模拟)

直接模拟即可。

代码(C++)

答案:11

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
	int x = 2022;
	
	string s;
	while(x) {
		s += x % 2 + '0';
		x /= 2;
	}
	
//	cout << s << endl;
	cout << s.size() << endl;
	
	return 0;
}

B题-晨跑(填空题)

题面

2022这一年中,小明会在星期六和星期天以及每个月的1、11、21、31号进行跑步,现在已知2022年1月1日为星期六,求这一年中小明跑步的天数。

算法(枚举)

模拟赛的时候是直接用日历数的。

代码(c++)

答案:138

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
	cout << 13 + 11 + 12 + 12 + 11 + 10
		+ 13 + 11 + 10 + 13 + 11 + 11 << endl;
	return 0;
}

C题-调和级数(填空题)

题面

求调和级数的累加和 S S S大于12的最小项数。

算法(二分)

很直观的是,调和级数会随着项数的增加,它的累加和 S S S会单调增加,二分即可。

代码(c++)

答案:91380

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

double get(int n)
{
	double res = 0;
	for(int i = 1; i <= n; i ++) {
		res += 1.0 / (i * 1.0);
	}
	return res;
}

int main()
{
	double res = 0;
	for(int i = 1; i <= 200000; i ++) {
		res += 1.0 / (i * 1.0);
	}
	
	int l = 0, r = 200000;
	while(l < r)
	{
		int mid = l + r >> 1;
		if(get(mid) > 12) r = mid;
		else l = mid + 1;
	}

//	cout << get(r - 1) << endl;
//	cout << get(r) << endl;
	
	cout << r << endl;
	return 0;
}

D题-山谷(填空题)

题面

在一个大写英文字母构成的 30 × 60 30 \times 60 30×60矩阵中,定义一个山谷为:一个非矩阵边界的位置,并且该位置的字母都小于其上下左右的字母。请计算该矩阵中山谷的个数。(大写字母的从小到大的排序: A B C . . . X Y Z ABC...XYZ ABC...XYZ)

本题的输入在文章底部。

算法(枚举)

枚举每一个非边界的位置,并判断其上下左右的字母是否都大于其本身的字母值即可。

代码(c++)

答案:276

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 65;

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n = 30, m = 60;
char g[N][N];

bool check(int x, int y)
{
	for(int i = 0; i < 4; i ++)
	{
		int a = x + dx[i], b = y + dy[i];
		if(g[a][b] <= g[x][y]) return false;
	}
	return true;
}

int main()
{
	for(int i = 1; i <= 30; i ++) cin >> g[i] + 1;

//	for(int i = 1; i <= n; i ++)
//	{
//		for(int j = 1; j <= m; j ++)
//			cout << g[i][j];
//		puts("");
//	}
//	
	int res = 0;
	for(int i = 2; i <= n - 1; i ++)
		for(int j = 2; j <= m - 1;j ++)
		{
			if(check(i, j)) res ++;
		}
	cout << res << endl;
	return 0;
}

E题-最小矩阵(填空题)

题面

给定 100 × 100 100 \times 100 100×100的矩阵,该矩阵第一行第一列的值为 1 1 1, 其余每一个位置的值满足:比其左边的大2,比其上面的大1。输该矩阵个矩阵中子矩阵的和等于 2022 2022 2022的子矩阵大小(行数 × \times ×列数),并且该输出的值需要最小。

算法(枚举,二维前缀和)

暴力是 1 e 8 × 100 × 100 1e8 \times 100 \times 100 1e8×100×100,程序跑不出来,可以在计算某一个子矩阵的和时,使用二维前缀和进行优化。

代码(C++)

答案:12

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int n= 100, m = 100;
int g[N][N];
int s[N][N];

int get(int a, int b, int c, int d)
{
	return s[c][d] - s[a - 1][d] - s[c][b - 1] + s[a - 1][b - 1];
}

int main()
{
	g[1][1] = 1;
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++)
		{
			if(i == 1 && j == 1) continue;
			if(i == 1)
			{
				g[i][j] = g[i][j - 1] + 2;
			}
			if(j == 1)
			{
				g[i][j] = g[i - 1][j] + 1;
			}
			if(i > 1 && j > 1)
			{
				g[i][j] = g[i - 1][j] + 1;
			}
		}

//	for(int i = 1; i <= n; i ++)
//	{
//		for(int j = 1; j <= m; j ++)
//			cout << g[i][j] << ' ';
//		puts("");
//	}

	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			s[i][j] = s[i - 1][j] + s[i][j - 1] + g[i][j] - s[i - 1][j - 1];

	int res = 1e9;
	for(int a = 1; a <= n; a ++)
		for(int b = 1; b <= m; b ++)
			for(int c = a; c <= n; c ++)
				for(int d = b; d <= m; d ++)
				{
					if(get(a, b, c, d) == 2022)
					{
						res = min(res, (c - a + 1) * (d - b + 1));
					}
				}

	cout << res << endl;

	return 0;
}

F题-核酸日期

题面

如果周一做核酸,周二显示核酸天数为 1 1 1天,周三显示 2 2 2天,以此类推,周六显示 5 5 5天,周日显示 6 6 6天。小蓝在某一天做了一次核酸,请问他的核酸显示为几天。已知做核酸和查看核酸不是在同一天,而且相差不超过 6 6 6天。(显示的数为 1 1 1 6 6 6之间的数)

输入格式

输入第一行包含一个整数 s s s,表示小蓝做核酸是周几。 s s s 1 1 1 6 6 6依次表示周一到周六, s s s 7 7 7表示周日。
第二行包含一个整数 t t t,表示查看核酸是周几。 t t t 1 1 1 6 6 6依次表示周一到周六, t t t 7 7 7表示周日。

输出格式

输出一行包含一个整数,表示答案。

数据范围

1 < = s , t < = 7 1 < = s , t < = 7 1<=s,t<=7

算法(模拟)

阅读理解后,模拟即可。

代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int n, m;

int main()
{
	cin >> n >> m;
	if(m < n) m += 7;
	cout << m - n << endl;
	return 0;
}

G题-英文转化

题面

输入一个由小写英文字母组成的字符串,请将其中的元音字母( a a a, e e e, i i i, o o o, u u u)转换成大写,其它字母仍然保持小写。

输入格式

输入一行包含一个字符串。

输出格式

输出转换后的字符串。

数据范围

字符串的长度不超过100。

算法(模拟)

从前往后枚举每一个字母,如果当前字母为元音字母,则将其变为大写,否则跳过。

代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

bool check(char c)
{
	if(c == 'a') return true;
	if(c == 'e') return true;
	if(c == 'i') return true;
	if(c == 'o') return true;
	if(c == 'u') return true;
	return false;
}

int main()
{
	string s;
	cin >> s;
	for(int i = 0; i < s.size(); i ++)
	{
		char c = s[i];
		if(check(c)) s[i] -= 32;
	}
	cout << s << endl;
	return 0;
}

H题-充电器

题面

小蓝有一个充电器,可以使用不同的电压和电流充电。
给定充电器工作的记录,请计算在这个记录期间总共通过充电传输了多少电能。

输入格式

输入第一行包含一个整数 n n n , 表示记录的条数。
接下来 n n n行,每行包含一个时刻 T T T和两个非负整数 U , I U, I U,I,表示在时刻 T T T充电电压变为 U U U(单位伏),电流变为 I I I(单位 A A A)。最后一行满足 U U U I I I均为 0 0 0,在前面的行中也可能出现 U 、 I U、I UI 0 0 0的情况。其中时间表示为 H H : M M : S S HH:MM:SS HH:MM:SS的格式,时分秒分别用两位十进制数表示(补前导零)。
输入保证时刻依次递增且在 00 : 00 : 00 00:00:00 00:00:00 23 : 59 : 59 23:59:59 23:59:59的区间内,不用考虑跨过零点充电的情况。

输出格式

输出一个整数,表示总共通电的电能为多少焦耳,其中 1 1 1焦耳等于 1 1 1伏乘以 1 1 1安乘以 1 1 1秒。

数据范围

1 < = n < = 100 , 0 < = U , I < = 100 1 < = n < = 100 , 0 < = U , I < = 100 1<=n<=100,0<=U,I<=100

算法(模拟)

阅读题目后,可以知道两个时间节点之间的电压 U U U和电流 A A A是固定的,所以我们枚举两个相邻的时间节点即可得到这段时间内的通电电能。具体的计算是: E = U × A × t i m e E=U \times A \times time E=U×A×time,在模拟的过程中我们累加每一段时间内的电能即可得到答案。

代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int n;
string time[N];
int U[N], A[N];

int calc(int h, int m, int s)
{
	return h * 3600 + m * 60 + s;
}

// HH:MM:SS
int get(string st, string ed)
{
	int h1 = stoi(st.substr(0, 2)), m1 = stoi(st.substr(3, 2)), s1 = stoi(st.substr(6, 2));
	int h2 = stoi(ed.substr(0, 2)), m2 = stoi(ed.substr(3, 2)), s2 = stoi(ed.substr(6, 2));
	return calc(h2, m2, s2) - calc(h1, m1, s1);
}

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> time[i] >> U[i] >> A[i];
	
	int res = 0;
	for(int i = 1; i + 1 <= n; i ++)
	{
		int deleta = get(time[i], time[i + 1]);
		res += deleta * (U[i] * A[i]);
	}
	
	cout << res << endl;
	return 0;
}

I题-全相等三角形

题面

给定一个字母矩阵,定义一个 L Q LQ LQ三角形为某行中连续的几个字母、某列中连续的几个字母和一条 45 45 45度的斜线中连续的几个字母组成的等腰直角三角形的边缘部分,其中每条边上的字母数量相等且至少为 2 2 2
  例如,对于下面的字母矩阵中,所有的字母 L L L组成一个 L Q LQ LQ三角形,所有字母 Q Q Q组成了一个 L Q LQ LQ三角形,所有字母 C C C也组成了一个 L Q LQ LQ三角形。

AAAAAAA  
ALLLLLA   
ALQQLAA   
ALQLAAC   
ALLAACC   
ALAACCC

如果一个 L Q LQ LQ三角形边上的所有字母相等,则称为一个全相等三角形。以三个例子都是全相等三角形。
给定一个字母矩阵,请求其中有多少个全相等三角形。

输入格式

输入第一行包含两个整数 n , m n, m n,m,分别表示字母矩阵的行数和列数。
接下来 n n n行,每行 m m m个大写字母,为给定的矩阵。

输出格式

输出一行,包含一个整数,表示答案。

数据范围

1 < = n , m < = 100 1<=n,m<=100 1<=n,m<=100

算法(枚举)

由于本题的数据量较小,可以直接枚举等腰直角三角形的斜边是那两个点,在确定斜边后,我们可以试着判断其可能构成的等腰直角三角形的形状,具体的,如果我们枚举的斜边是 45 45 45度,那么我们需要判断其左上以及右上方向;另外一种情况,我们枚举的斜边是 135 135 135度,那么我们需要判断其左下以及右上方向。这种枚举的方案是不重不漏的,细节见代码。

代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>

using namespace std;

const int N = 110;

int n, m;
char g[N][N];

bool check_L(int x1, int y1, int x2, int y2)
{
	unordered_set<char> S;
	int a = x1, b = y1;
	while(!(a == x2 && b == y2))
	{
		S.insert(g[a][b]);
		a --, b ++;
	}
	
	if(S.size() != 1) return false;
	char c = *S.begin();
	for(int i = x2; i <= x1; i ++)
		if(g[i][y1] != c) return false;
	for(int i = y1; i <= y2; i ++)
		if(g[x2][i] != c) return false;
	return true;
}

bool check_R(int x1, int y1, int x2, int y2)
{
	unordered_set<char> S;
	int a = x1, b = y1;
	while(!(a == x2 && b == y2))
	{
		S.insert(g[a][b]);
		a --, b ++;
	}

	if(S.size() != 1) return false;
	char c = *S.begin();
	for(int i = x2; i <= x1; i ++)
		if(g[i][y2] != c) return false;
	for(int i = y1; i <= y2; i ++)
		if(g[x1][i] != c) return false;
	return true;
}

bool check_1(int x1, int y1, int x2, int y2)
{
	unordered_set<char> S;
	int a = x1, b = y1;
	while(!(a == x2 && b == y2))
	{
		S.insert(g[a][b]);
		a ++, b ++;
	}

	if(S.size() != 1) return false;
	char c = *S.begin();
	for(int i = x1; i <= x2; i ++)
		if(g[i][y1] != c) return false;
	for(int i = y1; i <= y2; i ++)
		if(g[x2][i] != c) return false;
	return true;
}

bool check_2(int x1, int y1, int x2, int y2)
{
	unordered_set<char> S;
	int a = x1, b = y1;
	while(!(a == x2 && b == y2))
	{
		S.insert(g[a][b]);
		a ++, b ++;
	}

	if(S.size() != 1) return false;
	char c = *S.begin();
	for(int i = x1; i <= x2; i ++)
		if(g[i][y2] != c) return false;
	for(int i = y1; i <= y2; i ++)
		if(g[x1][i] != c) return false;
	return true;
}

int main()
{
	cin >> n >> m;
	for(int i = 0; i < n; i ++) cin >> g[i];
	
	int res = 0;
	for(int i = 1; i <= n + m - 3; i ++)
	{
		for(int d = min(i, n - 1); d >= max(0, i - m + 1); d --)
		{
			for(int u = d - 1; u >= 0; u --)
			{
				int x1 = d, y1 = i - d;
				int x2 = u, y2 = i - u;
				if(check_L(x1, y1, x2, y2))
				{
					res ++;
				}
				if(check_R(x1, y1, x2, y2))
				{
					res ++;
				}
			}
		}
	}
	
	for(int i = n - 2; i >= 0; i --)
	{
		for(int u = i; u <= n - 1; u ++)
			for(int d = u + 1; d <= n - 1; d ++)
			{
				int x1 = u, y1 = 0;
				int x2 = d, y2 = y1 + (d - u);
				if(check_1(x1, y1, x2, y2)) res ++;
				if(check_2(x1, y1, x2, y2)) res ++;
			}
	}
	
	for(int i = 1; i <= m - 2; i ++)
	{
		for(int u = i; u <= m - 2; u ++)
			for(int d = u + 1; d <= m - 2; d ++)
			{
				int x1 = 0, y1 = u;
				int x2 = d - u, y2 = d;
				if(check_1(x1, y1, x2, y2)) res ++;
				if(check_2(x1, y1, x2, y2)) res ++;
			}
	}
	
	cout << res << endl;
	return 0;
}

J题-最小下标

题面

小蓝有一个由大写字母 A B C D E F ABCDEF ABCDEF组成的字符串 S S S,长度为 n n n,字符串的下标依次为 0 0 0 n − 1 {n-1} n1
小蓝按照如下方法生成一个无限长的字符串:
首先选定一个 0 0 0 n − 1 {n-1} n1之间的数,作为初始下标。
从初始下标开始,将下标对应的字符加入到字符串的结尾,将字符的序号( A A A F F F依次对应 1 1 1 6 6 6)与下标相加作为新的下标值,如果下标大于等于 n n n,将其对 n n n求余。重复此过程,即得到无限长的字符串。
例如,对于字符串 A C D F ACDF ACDF,当初始下标是 0 0 0时,生成的字符串为: A C A C A C A C A C … ACACACACAC… ACACACACAC
再如,对于字符串 D C B A DCBA DCBA,当初始下标是 1 时,生成的字符串为: C D D D D D D D D D … CDDDDDDDDD… CDDDDDDDDD
给定小蓝的字符串 S S S,请问当初始下标为多少时,生成的字符串最小。

输入格式

输入一行包含一个字符串。

输出格式

输出一行,包含一个整数,为所求的下标,如果有多个下标满足要求,输出最小的那个。

数据范围

1 < = ∣ S ∣ < = 1000000 1<=|S|<=1000000 1<=S<=1000000

算法(贪心,模拟)

题目要求输出从某一个下标开始能够按题面的规则构成的最小字符串(题面没有过多解释这个最小,但应该理解成字典序最小)。我们考虑以下几种情况:

  • 情况一:所给出的字符串中存在唯一最小的字母,直接输出该字母的下标即可。
  • 情况二:所给出的字符串中所有字母都是同一个字母,直接输出最小的下标 0 0 0
  • 情况三:所给出的字符串中最小的字母出现次数不止一次,我们可以存在最小字母的下标,然后按照题面的规则进行字符串的构造,这里我们可以每次对所有最小的字母都进行一次构造,再将这轮构造结果所在的位置存下来,并且再次寻找最小字母,如果最小字母出现次数为 1 1 1,回到情况一,结束;如果最小字母出现次数不止一次,则反复执行情况三。

由于是贪心解法,模拟赛中没有进行严谨的证明,应该能过 70 70 70%~ 80 80 80%
数据。

代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>

using namespace std;

string s;

bool isAll(string s)
{
	unordered_set<char> S;
	for(int i = 0; i < s.size(); i ++) S.insert(s[i]);
	return S.size() == 1;
}

int main()
{
	cin >> s;
	int n = s.size();
	
	if(isAll(s))
	{
		cout << 0 << endl;
		return 0;
	}
	
	unordered_set<int> S;
	char c = 'Z';
	for(int i = 0; i < n; i ++) c = min(c, s[i]);
	for(int i = 0; i < n; i ++)
		if(c == s[i]) S.insert(i);

	if(S.size() == 1) cout << *S.begin() << endl;
	else {
		unordered_set<int> tmp;
		while(true)
		{
			for(auto& x : S)
			{
				tmp.insert((s[x] - 'A' + 1 + x) % n);
			}
			
			S.clear();
			
			c = 'Z';
			for(auto& x : tmp) c = min(c, s[x]);
			for(auto& x : tmp)
				if(c == s[x]) S.insert(x);
			if(S.size() == 1)
			{
				cout << *S.begin() << endl;
				break;
			}
			S = tmp;
			tmp.clear();
		}
	}

	return 0;
}

附录

D题输入

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
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值