2020年第十一届蓝桥杯C/C++A组题解(不含最后两题)

A 门牌制作

题目描述

计算从1到2020有多少个字符2。

思路

数据量很小,直接暴力,对每个数字按位统计2的数量即可。

代码

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int ans = 0;
	for (int i = 1; i <= 2020; i++) {
		int x = i;
		while (x) {
			if (x % 10 == 2)ans++;
			x /= 10;
		}
	}
	cout << ans << endl;
    return 0;
}

答案

624

B 既约分数

题目描述

既约分数的定义:一个分数的分子和分母的最大公约数是1。
计算有多少个既约分数的分子和分母都是1到2020之间的整数。

思路

数据量很小,直接暴力,分子从1到2020,分母从1到2020,判断分子分母组合成的2020×2020个数中有多少个满足分子分母互质。

代码

#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a%b);
}
int main()
{
	int ans = 0;
	for (int i = 1; i <= 2020; i++) {
		for (int j = 1; j <= 2020; j++) {
			if (gcd(i, j) == 1)ans++;
		}
	}
	cout << ans << endl;
    return 0;
}

答案

2481215

C 蛇形填数

题目描述

如下图所示,小明用从 1 开始的正整数“蛇形”填充无限大的矩阵。
1 2 6 7 15 …
3 5 8 14 …
4 9 13 …
10 12 …
11 …

容易看出矩阵第二行第二列中的数是5。请你计算矩阵中第 20 行第 20 列的数是多少?

思路

将蛇形矩阵顺时针旋转45度得:
1
3 2
4 5 6
10 9 8 7
11 12 13 14 15
……
每行的数字数量等于当前行的行数,奇数行从左至右填充,偶数行从右至左填充。原(1,1)对应现(1,1),原(2,2)对应现(3,2),原(3,3)对应现(5,3),……,推知原(x,x)对应现(2x-1,x)。
故原第20行第20列对应现第39行第20列。

代码

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 40;
int a[maxn][maxn];
int main()
{
	int cur = 1;
	for (int i = 1; i <= 40; i++) {
		int cnt = 0;
		if (i & 1) {
			for (int j = cur + i - 1; j >= cur; j--)a[i][++cnt] = j;
		}
		else {
			for (int j = cur; j < cur + i; j++)a[i][++cnt] = j;
		}
		cur += i;
	}
	cout << a[39][20] << endl;
    return 0;
}

答案

761

D 七段码

题目描述

题目描述

思路

模拟建图,注意建图时三个晶体管公共连接点,竖向的其中一个晶体管需要占据两个格子,并且采用dfs搜索时要向八个方向搜索,才可以将这三者之间的连接关系表示出来。七个晶体管选与不选共127种情况,每种情况可以压缩成一个二进制数,这个二进制数按照一定方式处理可以解码得到相应状态。采取这样的方式可以节省代码量。

代码

#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1 << 7;
bool mp[6][4], vis[6][4];
int dir[8][2] = { {1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1} };
void build(int x) {
	memset(mp, 0, sizeof(mp));
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= 5; i++) {
		if (i & 1)mp[i][2] = x & 1;
		else{
			mp[i][1] = mp[i + 1][1] = x & 1;
			x = x >> 1;
			mp[i][3] = mp[i + 1][3] = x & 1;
		}
		x = x >> 1;
	}
}
void dfs(int x, int y) {
	vis[x][y] = 1;
	for (int i = 0; i < 8; i++) {
		int nx = x + dir[i][0];
		int ny = y + dir[i][1];
		if (nx >= 1 && nx <= 5 && ny >= 1 && ny <= 3) {
			if (mp[nx][ny] && !vis[nx][ny])dfs(nx, ny);
		}
	}
}
int main()
{
	int ans = 0;
	for (int i = 1; i < maxn; i++) {
		build(i);
		int num = 0;
		for (int j = 1; j <= 5; j++) {
			for (int k = 1; k <= 3; k++) {
				if (mp[j][k] && !vis[j][k])dfs(j, k), num++;
			}
		}
		if (num == 1)ans++;
	}
	cout << ans << endl;
    return 0;
}

答案

80

E 平面分割

题目描述

20 个圆和20 条直线最多能把平面分成多少个部分?

思路

①由于圆和直线具有各自的性质,因此单独考虑二者。求直线的最大分割领域是好求的。一条直线一定可以与当前存在的所有直线全部交1次。一共交n次,就可将原n+1个区域分割成2×(n+1)个区域,即区域数加n+1。因此只需考虑每次划分平面时,当前有多少条直线。
②一个圆一定可以与当前存在的所有直线和圆全部交两次,这是由于圆具有封闭曲线的特性。每交两次,就可将原n个区域分割为2×n个区域,即区域数翻倍。因此只需考虑每次划分平面时,当前已经有多少个圆和多少条直线。

推导

①1条直线,分1+1个区域
(1)1个圆交直线,分2+2×1个区域
(2)2个圆交直线,分2+2×1+2×(1+1)个区域
(3)3个圆交直线,分2+2×1+2×(1+1)+2×(1+2)个区域
……
②2条直线相交,分1+1+2个区域
(1)1个圆交直线,分4+2×2个区域
(2)2个圆交直线,分4+2×2+2×(1+2)个区域
……
③3条直线互交,分1+1+2+3个区域
……
④4条直线互交,分1+1+2+3+4个区域
……
综上,n条直线互交,分1+1+2+3+4+……+n=1+(1+n)×n/2个区域
m个圆交直线,分1+(1+n)×n/2+2×n+2×(n+1)+2×(n+2)+……+2×(n+m-1)= 1+(1+n)×n/2+2×n×m+2×(1+2+……+m-1)= 1+(1+n)×n/2+2×n×m+m×(m-1)= 1+(1+n)×n/2+2×n×m+m×(m-1)个区域,将n=m=20代入,得最多分1+21×10+2×20×20+20×19=1391个区域。

答案

1391

F 成绩分析

题目描述

一次考试,每个学生的得分都是一个0到100的整数,请计算这次考试的最高分、最低分、平均分。

思路

注意计算平均分时开float,保留两位小数,其它的模拟即可。

代码

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
	ll n;
	cin >> n;
	ll x;
	ll maxn = -1, minn = 1e9, sum = 0;
	for (ll i = 1; i <= n; i++) {
		cin >> x;
		maxn = max(maxn, x), minn = min(minn, x);
		sum += x;
	}
	cout << maxn << endl << minn << endl;
	double ans = sum*1.0 / n;
	ans *= 1000;
	if (ll(ans) % 10 >= 5) {
		ll y = 10 - (ll(ans) % 10);
		ans += y;
	}
	double a = ans*1.0 / 1000;
	printf("%.2lf\n", a);
    return 0;
}

G 回文日期

题目描述

给出一个日期,求该日期之后离它最近的回文日期和ABABBABA型日期分别是什么。

思路

为了方便,由于数据量不是那么大,直接暴力遍历给出的日期+1到99991231之间的所有可能的日期,逐一判断其是否合法。注意闰年是4的倍数且不是100的倍数,或者是400的倍数。

代码

#include<iostream>
using namespace std;
bool ishuiwen(int year, int month, int day) {
	if (year / 1000 != day % 10)return false;
	if ((year % 1000) / 100 != day / 10)return false;
	if ((year % 100) / 10 != month % 10)return false;
	if (year % 10 != month / 10)return false;
	return true;
}
bool isABABBABA(int year, int month, int day) {
	if (year / 1000 != (year % 100) / 10)return false;
	if (year / 1000 != month % 10)return false;
	if (year / 1000 != day % 10)return false;
	if (day / 10 != month / 10)return false;
	if (day / 10 != year % 10)return false;
	if (day / 10 != (year % 1000) / 100)return false;
	return true;
}
int get_year(int cal) {
	int st = cal / 10000000;
	int nd = (cal % 10000000) / 1000000;
	int rd = (cal % 1000000) / 100000;
	int th = (cal % 100000) / 10000;
	int year = st * 1000 + nd * 100 + rd * 10 + th;
	return year;
}
int get_month(int cal) {
	int st = (cal % 10000) / 1000;
	int nd = (cal % 1000) / 100;
	int month = st * 10 + nd;
	return month;
}
int get_day(int cal) {
	int st = (cal % 100) / 10;
	int nd = cal % 10;
	int day = st * 10 + nd;
	return day;
}
bool islegal(int year, int month, int day) {
	if (month >= 13 || month <= 0)return false;
	if (day <= 0)return false;
	if (month == 4 || month == 6 || month == 9 || month == 11) {
		if (day >= 31)return false;
	}
	else if (month != 2) {
		if (day >= 32)return false;
	}
	else {
		if (year % 4 == 0 && year % 100 || year % 400 == 0) {
			if (day >= 30)return false;
		}
		else {
			if (day >= 29)return false;
		}
	}
	return true;
}
int main()
{
	int cal;
	cin >> cal;
	int st = 0, nd = 0;
	for (int cur = cal + 1; cur <= 99991231; cur++) {
		int year = get_year(cur);
		int month = get_month(cur);
		int day = get_day(cur);
		if (!islegal(year, month, day))continue;
		if (ishuiwen(year, month, day) && !st)st = cur;
		if (isABABBABA(year, month, day) && !nd)nd = cur;
		if (st&&nd)break;
	}
	cout << st << endl << nd << endl;
    return 0;
}

H 子串分值

题目描述

给一个字符串S,定义S的分值f(S)为S中恰好出现一次的字符个数。设串长为n,计算对于S的所有非空子串S[i……j](0<=i<=j<=n),f(S[i……j])的和是多少。

思路

要只遍历一遍就可以求出答案,那么突破口在于只有26种字符,完全可以对每个字符求一遍贡献。子串可以分为n类,每一类分别以第i号元素为结尾。
设所求字符为a,若第i号元素及之前没有出现a,则对于此类子串,a的贡献为0;若第i号元素及之前a出现过一次,则对于此类子串,a的贡献等于a出现的位置;若第i号元素及之前a出现过两次及以上,则对于此类子串,a的贡献等于离第i号元素最近两次a出现的位置之差。因此我们需要记录在第i号元素之前最近两次各元素出现的位置。最后将各种字符的贡献相加即得结果。

代码

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s;
	cin >> s;
	int ans = 0;
	for (int i = 0; i < 26; i++) {
		char x = 'a' + i;
		int last = -1, cur = -1;
		for (int j = 0; j < s.size(); j++) {
			if (s[j] == x) {
				if (cur == -1 && last == -1)cur = j + 1;
				else if (cur != -1 && last == -1)last = cur, cur = j + 1;
				else if (cur != -1 && last != -1)last = cur, cur = j + 1;
			}
			if (cur != -1 && last != -1)ans += (cur - last);
			else if (cur != -1 && last == -1)ans += cur;
		}
	}
	cout << ans << endl;
    return 0;
}

I、J两题

难度太高,没有研究明白,这里就不给出题解了。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

keguaiguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值