第十三届蓝桥杯模拟赛(第四期)试题与部分题解 C++

第四期模拟赛题目

试题A

【问题描述】

   以下是一个 25 行 25 列的字母矩阵,全部由字母 A 和 B 组成。

  AAAAAAABABBAABABABAAAAAAA
  ABBBBBABBAABBBBBABABBBBBA
  ABAAABABBBABAABBBBABAAABA
  ABAAABABBBBBAABAABABAAABA
  ABAAABABBABABBABABABAAABA
  ABBBBBABBBABAABBBBABBBBBA
  AAAAAAABABABABABABAAAAAAA
  BBBBBBBBABAABABBBBBBBBBBB
  AABAABABBAAABBAAABABBBBBA
  ABBABABBBABBAAAABBBBAAAAB
  BBBBAAABABAABABAABBBAABBA
  BBAABABABAAAABBBAABBAAAAA
  ABABBBABAABAABABABABBBBBA
  AAAABBBBBABBBBAAABBBABBAB
  AABAABAAABAAABAABABABAAAA
  ABBBBBBBBABABBBBABAABBABA
  ABBBAAABAAABBBAAAAAAABAAB
  BBBBBBBBABBAAABAABBBABBAB
  AAAAAAABBAAABBBBABABAABBA
  ABBBBBABBAABABAAABBBABBAA
  ABAAABABABBBAAAAAAAAAABAA
  ABAAABABABABBBABBAABBABAA
  ABAAABABBABBABABAABAABAAA
  ABBBBBABABBBBBABBAAAABAAA
  AAAAAAABAABBBAABABABBABBA

   请问在这个矩阵中有多少个字母A?

【答案提交】

   这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

题解:计数

​ 因为原题目里的矩阵每一行前就自带空格,所以就直接循环输入了,粘贴之后按 ctrl + z 回车结束输入

//答案 318
#include <bits/stdc++.h> 
using namespace std;

int main() {
	int ans = 0;
	char ch;
	while (cin >> ch) {
		ans += (ch == 'A');
	}
	cout << ans << endl;
	return 0;
}

试题B

【问题描述】

   如果一个整数的某个数位包含 2 ,则称这个数为一个“最2数字”。例如:102、2021 都是最2数字。
  请问在 1(含) 到 2021(含) 中,有多少个最2数字。

【答案提交】

   这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

题解:数字拆分

​ 数字拆分,蓝桥杯经常出的题,今年省赛很可能也会出

//答案 564
#include <bits/stdc++.h> 
using namespace std;

bool check(int n) {
	while (n) {
		if (n % 10 == 2) return true;
		n /= 10;
	}
	return false;
}

int main() {
	int ans = 0;
	for (int i = 1; i <= 2021; i++) {
		ans += check(i);
	}
	cout << ans << endl;
	return 0;
}

试题C

【问题描述】

   有一个整数 A=2021,每一次,可以将这个数加 1 、减 1 或除以 2,其中除以 2 必须在数是偶数的时候才允许。
  例如,2021 经过一次操作可以变成 2020、2022。
  再如,2022 经过一次操作可以变成 2021、2023 或 1011。
  请问,2021 最少经过多少次操作可以变成 1。

【答案提交】

   这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

方法一:广搜

​ 最少操作 14 次:

​ 2021 --> 2020 --> 1010 --> 505 --> 504 --> 252 --> 126 --> 63 --> 64 --> 32 --> 16 --> 8 --> 4 --> 2 --> 1

//答案 14
#include <bits/stdc++.h> 
using namespace std;

queue<int> q;
set<int> s;

int main() {
	q.push(2021);
	s.insert(2021);
	int ans = 0;
	while (!q.empty()) {
		int n = q.size();
		ans ++;
		while (n--) {
			int cur = q.front();
			q.pop();
//			cout << cur << " " << ans - 1 << endl;
			if (cur == 1) {
				cout << ans - 1 << endl;
				return 0;
			}
			if (!s.count(cur + 1)) {
				q.push(cur + 1);
				s.insert(cur + 1);
			}
			if (!s.count(cur - 1)) {
				q.push(cur - 1);
				s.insert(cur - 1);
			}
			if (cur % 2 == 0 && !s.count(cur / 2)) {
				q.push(cur / 2);
				s.insert(cur / 2);
			}
		}
	}
	return 0;
}

方法二:迪杰斯特拉/单源最短路

​ 就当作权值都为 1 的单源最短路问题

​ 模拟的时候写的最短路,但是忘记写 min 函数得出来个 17,也没怎么验证,总之就是非常后悔(明明深搜广搜就可以,搞什么迪杰斯特拉)

//答案 14
#include <bits/stdc++.h> 
using namespace std;

int mp[2050];
int vis[2050];

bool check(int idx) {
	return idx >= 1 && idx <= 2048;
}

int main() {
	memset(mp, 0x3f, sizeof(mp));
	mp[2021] = 0;
	while (!vis[1]) {
		int idx = 0;
		for (int i = 0; i < 2050; i++) {
			if (mp[i] < mp[idx] && !vis[i]) idx = i;
		}
		vis[idx] = 1;
//		cout << idx << " " << mp[idx] << endl;
		if (check(idx - 1)) mp[idx - 1] = min(mp[idx - 1], mp[idx] + 1);
		if (check(idx + 1)) mp[idx + 1] = min(mp[idx + 1], mp[idx] + 1);
		if (idx % 2 == 0 && check(idx / 2)) mp[idx / 2] = min(mp[idx / 2], mp[idx] + 1);
	} 
	cout << mp[1] << endl;
	return 0;
}

方法三:贪心+深搜

容易想到,如果当前数字是偶数,就让它除以 2,这样一定优于加一减一

如果是奇数,可以让它先加一再除以二,也可以先减一再除以二,在两者中找最优解即可

//答案 14
#include <bits/stdc++.h>
using namespace std;

int dfs(int cur) {
	if (cur == 1) return 0;
	if (cur % 2 == 0) return dfs(cur / 2) + 1;
	return min(dfs(cur - 1), dfs(cur + 1)) + 1;
}

int main() {
	cout << dfs(2021) << endl;
	return 0;
}

试题D

【问题描述】

    对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
  例如,一个 4 行 5 列的螺旋矩阵如下:

 1   2   3   4   5
14  15  16  17   6
13  20  19  18   7
12  11  10   9   8

​    请问,一个 30 行 30 列的螺旋矩阵,第 20 行第 20 列的值是多少?

【答案提交】

   这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

题解:模拟

​ 定义上下左右四个下标边界,每次按照 上 右 下 左 的顺序填充边界线上的值,每填充一条边界,使该边界向内缩小一次

//答案 819
#include <bits/stdc++.h> 
using namespace std;

int a[32][32];
int k = 1;

int main() {
	int up = 1, down = 30, left = 1, right = 30;
	while (left <= right) {
		for (int i = left; i <= right; i++) {
			a[up][i] = k++;
		}
		up++;
		for (int i = up; i <= down; i++) {
			a[i][right] = k++;
		}
		right--;
		for (int i = right; i >= left; i--) {
			a[down][i] = k++;
		}
		down--;
		for (int i = down; i >= up; i--) {
			a[i][left] = k++;
		}
		left++;
	}
	/*for (int i = 1; i <= 30; i++) {
		for(int j = 1; j <= 30; j++) {
			printf("%5d", a[i][j]);
		}
		printf("\n");
	}*/
	cout << a[20][20] << endl;
	return 0;
}

试题E

【问题描述】

   一棵二叉树有2021个结点。该树满足任意结点的左子树结点个数和右子树的结点个数之差最多为1。
  定义根结点的深度为0,子结点的深度比父结点深度多1。
  请问,树中深度最大的结点的深度最大可能是多少?

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

题解:贪心

​ 贪心思想,每次都尽可能令左子树比右子树多 1 个节点(反之也可)

//答案 10
#include <bits/stdc++.h> 
using namespace std;

int main() {
	int cnt = 2021, d = 0;
	while (cnt != 1) {
		cnt--;
		cnt = cnt / 2 + (cnt % 2);
		d++;
	}
	cout << d << endl;
	return 0;
}

试题F

【问题描述】

   一个和尚要挑水,每次最多能挑 a 千克,水缸最多能装 t 千克,开始时水缸为空。
  请问这个和尚最少要挑多少次可以将水缸装满?

【输入格式】

输入一行包含两个整数 a, t,用一个空格分隔。

【输出格式】

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

【样例输入】

20 2021

【样例输出】

102

【评测用例规模与约定】

   对于所有评测用例,1 <= a <= 100,1 <= t <= 10000。

题解:基本运算

#include <bits/stdc++.h> 
using namespace std;

int main() {
	int a, t, ans;
	cin >> a >> t;
	ans = t / a + (t % a != 0);
	cout << ans << endl;
	return 0;
}

试题G

【问题描述】

   在金融领域,通常将金额的百位和千位之间、十万位和百万位之间增加逗号(千分位分隔符),以方便阅读。一般从个位开始,每三位之前增加一个逗号。
  例如:1234567890.00 通常写成 1,234,567,890.00。
  注意小数点后固定保留 2 位。
  给定一个包含千分位分隔符的数值,请读入后输出对应的不含千分位的数值,小数点仍然保留 2 位。

【输入格式】

输入一行包含一个由千分位分隔符的数值,恰好有 2 位小数。

【输出格式】

输出不含千分位分隔符的数值,保留 2 位小数。

【样例输入】

1,234,567,890.00

【样例输出】

1234567890.00

【评测用例规模与约定】

   对于所有评测用例,给定的数值整数部分不超过12位。

题解:输入输出

​ 输入字符串,输出时跳过逗号

#include <bits/stdc++.h> 

using namespace std;

int main() {
	string str;
	cin >> str;
	int n = str.length();
	for (int i = 0; i < n; i++) {
		if (str[i] != ',') cout << str[i];
	}
	cout << endl;
	return 0;
}

试题H

【问题描述】

   小蓝有一个插板,形状用一个 n * m 的01矩阵表示,0 表示板面,1 表示插孔。
  小蓝还有一个插头,形状用一个 r * c 的01矩阵表示,0 表示没有伸出的部分,1 表示伸出的部分。插头伸出的部分必须插在插孔里面。
  为了安全,插头插到面板上不能有任何部分超过插板边界(包括没有伸出的部分)。
  插头和插板都不能旋转,也不能翻转。请求出插头插入插板的合理位置。

【输入格式】

   输入的第一行包含两个整数 n, m。
  接下来 n 行,每行一个长度为 m 的01串,表示插板的形状。
  接下来一行包含两个整数 r, c。
  接下来 r 行,每行一个长度为 c 的01串,表示插头的形状。

【输出格式】

   如果插头没办法安全插入插板中,输出“NO”。否则输出两个数 a, b,表示插头的第 1 行第 1 列对应插板的第 a 行第 b 列。如果有多种情况满足要求,输出 a 最小的方案,如果 a 最小的方案有多个,输出在 a 最小的前提下 b 最小的方案。

【样例输入】

3 4
0110
0000
0000
3 3
000
010
000

【样例输出】

NO

【样例说明】

   在插头不超出范围的前提下无法插入。

【样例输入】

4 7
1110100
1101111
0001111
0000011
2 3
111
011

【样例输出】

2 4

【评测用例规模与约定】

对于 50% 的评测用例,2 <= n, m, r, c <= 20。
对于所有评测用例,2 <= n, m, r, c <= 100。

题解:遍历

数据范围不是很大,遍历查找

感谢评论区“再吃一个橘子”指正:
   题目中要求插头的 1 必须对应插板的 1 的位置,而对插头的 0 没有特殊要求(最初理解成了插头与插座必须相等),所以不合理的情况只有插座为 ‘0’ 而插头为 ‘1’,由于只有 ‘0’ 和 ‘1’ 两种状态,可以利用字符直接比较(‘0’ < ‘1’)

#include <bits/stdc++.h> 
using namespace std;

const int N = 105;

int m, n;
int r, c;
string a[N], b[N];

bool check(int x, int y) {
	for (int i = x; i < x + r; i++) {
		for (int j = y; j < y + c; j++) {
			if (a[i][j] < b[i - x][j - y]) return false;
		}
	}
	return true;
}

int main() {
	cin >> m >> n;
	for (int i = 0; i < m; i++) {
		cin >> a[i];
	}
	cin >> r >> c;
	for (int i = 0; i < r; i++) {
		cin >> b[i];
	}
	for (int i = 0; i <= m - r; i++) {
		for (int j = 0; j <= n - r; j++) {
			if (check(i, j)) {
				cout << i + 1 << " " << j + 1 << endl;
				return 0;
			}
		}
	}
	cout << "NO" << endl;
	return 0;
}

试题I

【问题描述】

给定正整数 a, b, c,请问有多少个正整数,是其中至少两个数的约数。

【输入格式】

输入一行包含三个正整数 a, b, c。

【输出格式】

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

【样例输入】

30 70 35

【样例输出】

6

【样例说明】

1、2、5、7、10、35满足条件。

【评测用例规模与约定】

对于 50% 的评测用例,1 <= a, b, c <= 1000000。
对于所有评测用例,a, b, c 不超过 10**12(10的12次方)。

题解:最大公约数的因子

分别求出 (a, b), (a, c), (b, c) 的最大公约数,公约数的所有因子就是符合题中条件的数

证明过程与质因数分解有关:将两个整数分解为质因数相乘的形式,两数的最大公约数是所有公共质因数的乘积,而其他公约数是公共质因数中的一部分相乘,所以其他的公约数一定是最大公约数的因子

循环到平方根求每个因子,降低复杂度,再用 set 去重,最后 set 里的元素个数就是答案

#include <bits/stdc++.h> 
using namespace std;
typedef long long ll;

set<ll> s;

ll gcd(ll a, ll b) {
	return (a % b == 0) ? b : gcd(b, a % b);
}

void func(ll num) {
	ll n = sqrt(num);
	for (ll i = 1; i <= n; i++) {
		if (num % i == 0) {
			s.insert(i);
			s.insert(num / i);
		}
	}
}

int main() {
	ll a, b, c;
	cin >> a >> b >> c;
	ll x, y, z;
	x = gcd(a, b);
	y = gcd(a, c);
	z = gcd(b, c);
	func(x);
	if (y != x) func(y);
	if (z != x && z != y) func(z);
	cout << s.size() << endl;
	/*for (set<ll>::iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << endl;
	}*/
	return 0;
}

试题J

【问题描述】
  小蓝很喜欢玩汉诺塔游戏。
  游戏中有三根柱子,开始时第一根柱子上有 n 个圆盘,从上到下圆盘的大小依次为 1 到 n。
  每次,可以将一个盘子从一根柱子上移动到另一根柱子上,这个盘子必须是柱子最上方的盘子,而且移到的柱子上的盘子必须比这个盘子大。
  小蓝的目标是将所有的盘子移动到第三根柱子上。
  汉诺塔是个经典问题,当盘子数量为 n 时,最少需要移动 2**n-1 步,其中 2**n 表示 2 的 n 次方。
  小蓝已经玩了一会儿(不一定按最优方案玩),他想知道,对于他目前的局面,最少还需要多少步可以到达目标。
【输入格式】
  输入的第一行包含三个非负整数 a, b, c,分别表示目前每根柱子上的盘子数。在本题中,n=a+b+c。
  第二行包含 a 个整数,相邻的整数之间使用一个空格分隔,表示第一根柱子上的盘子,盘子按从上到下(从小到大)的顺序给出。
  第三行包含 b 个整数,相邻的整数之间使用一个空格分隔,表示第二根柱子上的盘子,盘子按从上到下(从小到大)的顺序给出。
  第四行包含 c 个整数,相邻的整数之间使用一个空格分隔,表示第三根柱子上的盘子,盘子按从上到下(从小到大)的顺序给出。
【输出格式】
  输出一行包含一个整数,表示答案。
【样例输入】

1 2 3
1
2 3
4 5 6

【样例输出】

7

【评测用例规模与约定】
  对于 30% 的评测用例,2 <= n <= 5。
  对于所有评测用例,2 <= n <= 60。

不会做,但是根据题目觉得可能有快速幂,再看用例规模是 60,long long 是 64 位,可能有状态压缩

感觉思路是贪心,但想不出规则,求高人指点

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值