2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场

文章目录

    • 试题A:门牌制作
    • 试题B:既约分数
    • 试题C:蛇形填数
    • 试题D:跑步锻炼
    • 试题 E 七段码
    • 试题F:成绩统计
    • 试题G:回文日期
    • 试题H:子串分值和
    • 试题I:平面切分
    • 试题 J 字串排序


试题A:门牌制作

【问题描述】小蓝要为一条街的住户制作门牌号。这条街一共有2020位住户,门牌号从1到2020编号。小蓝制作门牌的方法是先制作0到9这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌1017需要依次粘贴字符1、0、1、7,即需要1个字符0,2个字符1,1个字符7。请问要制作所有的1到2020号门牌,总共需要多少个字符2?

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

#include
using namespace std;
typedef long long ll;

int main(void){
     
	int sum = 0;
	for(int i = 1;i<=2020;i++){
     
		int n = i;
		while(n){
     
			if(n%10==2){
     
				sum++;
			}
			n/=10;
		}
	}
	printf("%d\n",sum);
}

试题B:既约分数

【问题描述】如果一个分数的分子和分母的最大公约数是1,这个分数称为既约分数。例如,3/4,5/2,1/8,7/1都是既约分数。请问,有多少个既约分数,分子和分母都是1到2020之间的整数(包括1和2020)

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

需要注意的是这里是判断两个数的最大公约数是否为1,即gcd返回的是return b==0?a:gcd(b,a%b);
如果是要求最大公约数,则gcd返回的是return a%b==0?b:gad(b,a%b);

#include
using namespace std;
typedef long long ll;

//求最大公约数
int gcd(int a,int b){
     
	return b==0?a:gcd(b,a%b);
}

int main(void){
     
	ll sum = 0;
	for(int i = 1;i <= 2020;i++){
     
		for(int j = 1;j <= 2020;j++){
     
			//两数相等直接跳过(1/1的情况一会在加上)
			if(i == j){
     
				continue;
			}
			if(gcd(i,j) == 1){
     
				sum++;
			}
		}
	}
	//1/1的情况
	sum+=1;
	printf("%ld\n",sum);
}

试题C:蛇形填数

【问题描述】
如下图所示,小明用从1开始的正整数“蛇形”填充无限大的矩阵。容易看出矩阵第二行第二列中的数是5。请你计算矩阵中第20行第20列的数是多少?

2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场_第1张图片

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

2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场_第2张图片

可以理解成是从1开始,首先向右上走,也就是x–,y++;之后是往左下走,也就是x++,y–;在判断的地方取i是否为奇数或者是偶数是与上面相关,奇数则是按右上走,偶数为右下;关于赋初值,x,y是根据方向来看,一是确定不会出界,所以就是x–对应x从i开始减,直到大于等于1;y的话是从1开始增,直到小于等于i;偶数同理。

#include 
using namespace std;
int a[50][50],cnt=1;
int main(){
     
    for(int i = 1 ; i <= 40; i++){
     
        if(i % 2==1){
     
            for(int x = i, y = 1; x >= 1 && y <= i; x--, y++)
                a[x][y] = cnt++;
        }
        else{
     
            for(int x = 1, y = i; x <= i && y >= 1; x++, y--)
                a[x][y] = cnt++;
        }
    }
    printf("%d\n", a[20][20]);
	return 0;
}

二代代码

#include
int main(){
     
	int a[50][50]={
     0};
	int cnt=1;
	for(int i=1;i<=40;i++){
     
		if(i%2==1){
     
			for(int x=i,y=1;x>=1&&y<=i;x--,y++){
     
				a[x][y]=cnt++;
			}
		}
		else{
     
			for(int x=1,y=i;x<=i&&y>=1;x++,y--){
     
				a[x][y]=cnt++;
			}
		}
	}
	for(int i=1;i<=40;i++){
     
		for(int j=1;j<=40;j++){
     
			printf("%4d",a[i][j]);
		}
		printf("\n");
	}
	printf("%d",a[20][20]);
	return 0;
}

2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场_第3张图片

试题D:跑步锻炼

【问题描述】小蓝每天都锻炼身体。正常情况下,小蓝每天跑1千米。如果某天是周一或者月初(1日),为了激励自己,小蓝要跑2千米。如果同时是周一或月初,小蓝也是跑2千米。小蓝跑步已经坚持了很长时间,从2000年1月1日周六(含)到2020年10月1日周四(含)。请问这段时间小蓝总共跑步多少千米?

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

这里是流程
1.首先算出从2000/1/1~2020/10/1共多少天
2.已知2000/1/1此刻为周六,加7天,为一周,也就是有一个周一
3.去重,通过日历找月初,以及既是周一又是月初。

首先算出两者相隔的天数是7579天,因题目说第一天就开始跑了,所以加上1,总共跑的天数就是7580天,每天1公里,也就是7580公里了。
2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场_第4张图片
接着算上这7580天里共有几个周一,通过代码我们知道共有1083个周一,所以每周一再多跑1公里,总共就跑了7580+1083=8663公里。

#include
using namespace std;
int main(){
     
	int ans=0;
	for(int i=3;i<=7580;i+=7){
       //当i=1时,此时为周六,咱们从i=3,
	                           //即下周一开始算起
		ans++;
	}
	cout<<ans;
	return 0;
}

然后在算上从2000/1/1到2020/10/1共有多少个月初。从数据上看我们知道一共有20年+10个月,所以总月份数位20*12+10=250个月,也就是这250个月初里还要多跑250公里。所以总公里数为:8663+250=8913。
最后,如果月初和周一重合咱们也是只跑两公里的,通过日历我们知道共有34个重合数,所以总公里数为:8913-34==8879

试题 E 七段码

【问题描述】
小蓝要用七段码数码管来表示一种特殊的文字。
七段码上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二极管,分别标记为 a, b, c, d, e, f, g。小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?
2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场_第5张图片

这里是简介
主要程序类似连通图,就是判断相邻两个之间是否连通(相连)
引入vis是看有几条发光,类似手算时一个一个求。

#include
using namespace std;
int a,b,c,d,e,f,g,vis[7],ans=0;
int check(){
     
    int cnt=0;
    for(int i=0;i<7;i++)
    cnt+=vis[i];
    if(!cnt) return 0;
    else if(cnt==1||cnt==7)  return 1;
    else //有2~6根管子,判断他们的邻居是不是都没亮{
     
        if(a){
     
            if(!b&&!f) return 0;
        }
        if(b){
     
            if(!a&&!g&&!c) return 0;
        }
        if(c){
     
            if(!b&&!g&&!d) return 0;
        }
        if(d)
        {
     
            if(!e&&!c)  return 0;
        }
        if(e)
        {
     
            if(!g&&!d&&!f) return 0;
        }
        if(f){
     
            if(!a&&!g&&!e)  return 0;
        }
        if(g){
     
            if(!b&&!c&&!e&&!f) return 0;
        }
        return 1;
    }  
}
int main(){
     //七重循环
    for(a=0;a<2;a++)
    for(b=0;b<2;b++)
    for(c=0;c<2;c++)
    for(d=0;d<2;d++)
    for(e=0;e<2;e++)
    for(f=0;f<2;f++)
    for(g=0;g<2;g++){
     
        vis[0]=a;vis[1]=b;vis[2]=c;vis[3]=d;
        vis[4]=e;vis[5]=f;vis[6]=g;
        ans+=check();
    }
    cout<<ans-3;
    return 0;
}

试题F:成绩统计

【问题描述】
小蓝给学生们组织了一场考试,卷面总分为100分,每个学生的得分都是一个0到100的整数。如果得分至少是60分,则称为及格。如果得分至少为85分,则称为优秀。请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整数。

【输入格式】 输入的第一行包含一个整数n,表示考试人数。接下来n行,每行包含一个0至100的整数,表示一个学生的得分。

【输出格式】 输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分四舍五入保留整数。

【样例输入】

7
80
92
56
74
88
100
0

【样例输出】

71%
43%

主要引用四舍五入代码:(int)((ans1+0.005)*100)
例如0.013+0.005=0.018;*100=1.8,(int)1.8=1
若为0.017+0.005=0.022;*100=2.2,(int)2.2=2
实现了四舍五入。

#include
using namespace std;
int main(){
     
    int n,a[10005],i,cnt1=0,cnt2=0;
    cin>>n;
    for(i=0;i<n;i++){
     
        cin>>a[i];
        if(a[i]>=60) cnt1++;
        if(a[i]>=85) cnt2++;
    }
    double ans1=cnt1*1.0/(n*1.0);
    double ans2=cnt2*1.0/(n*1.0);
    cout<<(int)((ans1+0.005)*100)<<"%"<<endl;
    cout<<(int)((ans2+0.005)*100)<<"%"<<endl;
    return 0;
}

试题G:回文日期

【问题描述】
2020年春节期间,有一个特殊的日期引起了大家的注意:2020年2月2日。因为如果将这个日期按“yyyymmdd”的格式写成一个8位数是20200202,恰好是一个回文数。我们称这样的日期是回文日期。有人表示20200202是“千年一遇”的特殊日子。对此小明很不认同,因为不到2年之后就是下一个回文日期:20211202即2021年12月2日。也有人表示20200202并不仅仅是一个回文日期,还是一个ABABBABA型的回文日期。对此小明也不认同,因为大约100年后就能遇到下一个ABABBABA型的回文日期:21211212即2121年12月12日。算不上“千年一遇”,顶多算“千年两遇”。给定一个8位数的日期,请你计算该日期之后下一个回文日期和下一个ABABBABA型的回文日期各是哪一天。

【输入格式】 输入包含一个八位整数N,表示日期。

【输出格式】 输出两行,每行1个八位数。第一行表示下一个回文日期,第二行表示下一个ABABBABA型的回文日期。

【样例输入】

20200202

【样例输出】

20211202
21211212

【评测用例规模与约定】 对于所有评测用例,10000101 <= N <= 89991231,保证N是一个合法日期的8位数表示。

#include
#include
#include
using namespace std;

//判断是否是回文日期
bool huiwen(string str)
{
     
	string str1 = str;
	reverse(str1.begin(), str1.end());
	if (str1 != str) return false;
	return true;
}
//判断是否是AB型日期
bool ab(string str)
{
     
	if (str[0] == str[2] && str[1] == str[3] && str[0] != str[1])
		return true;
	return false;
}
//闰年判断
bool isleap(int y)
{
     
	if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) return true;
	return false;
}
int main()
{
     
	int n;
	cin >> n;
	int y, m, d;
	y = n / 10000;
	m = n % 10000 / 100;
	d = n % 100;
	int ans1 = 0, ans2 = 0;
	while (1)
	{
     
		d++;
		if (d == 32 && m == 12)
		{
     
			m = 1;
			d = 1;
			y++;
		}
		if (d == 32 && (m == 1	 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10))
			d = 1, m++;
		if (d == 31 && (m == 4 || m == 6 || m == 9 || m == 11))
			d = 1, m++;
		if (d == 30 && m == 2 && isleap(y))
			d = 1, m++;
		if (d == 29 && m == 2 && !isleap(y))
			d = 1, m++;

		int num = y * 10000 + m * 100 + d;
		string str = to_string(num);
		if (huiwen(str))
		{
     
			if (!ans1) ans1 = num;
			if (!ans2 && ab(str))
			{
     
				ans2 = num;
				break;
			}
		}
	}

	cout << ans1 << endl;
	cout << ans2 << endl;

	system("pause");
	return 0;
}

试题H:子串分值和

对于一个字符串 S,我们定义 S 的分值 f (S ) 为 S 中出现的不同的字符个
数。例如 f (”aba”) = 2, f (”abc”) = 3, f (”aaa”) = 1。
现在给定一个字符串 S [0::n − 1](长度为 n),请你计算对于所有 S 的非空
子串 S [i:: j](0 ≤ i ≤ j < n), f (S [i:: j]) 的和是多少。

【输入格式】 输入一行包含一个由小写字母组成的字符串S。

【输出格式】 输出一个整数表示答案。

【样例输入】

ababc

【样例输出】

28

【样例说明】

子串    f值
a        1
ab       2
aba      2
abab     2
ababc    3
 b       1
 ba		 2
 bab	 2
 babc 	 3
   a	 1
   ab	 2
   abc	 3
    b	 1
    bc	 2
     c	 1

2020年蓝桥杯C/C++ B组 (有手就行的题解)省赛第二场_第6张图片

#include
#include
using namespace std;
long long last[26];
int main(){
     
	string str;
	cin>>str;
	memset(last,-1,sizeof(last));
	int n=str.size();
	long long ans;
	int i;
	for(i=0;i<n;i++){
     
		ans+=(i-last[str[i]-'a'])*(n-i);
		last[str[i]-'a']=i;
	} 
	/*
	for (int i = 1; i <= n; i ++)
    {
        ans += (LL)(i - last[s[i]]) * (n - i + 1);
        last[s[i]] = i;
    }
    */
	cout<<ans<<endl;
} 

试题I:平面切分

【问题描述】
平面上有N 条直线,其中第i条直线是y = Ai x+Bi
请计算这些直线将平面分成了几个部分。

【输入格式】 输入的第一行包含一个整数N, 以下N行,包含两个证书 Ai,Bi

【输出格式】 一个整数代表答案

【样例输入】

31
1
2 2
3 3

【样例输出】

6
#include
#include
#include
using namespace std;
set<pair<double,double> > se;
const int N = 1005;
double A[N];
double B[N];
set<pair<double,double> > s;
set<pair<double,double> >::iterator it;
int main(){
     
	int n,i,j,x,y;
	cin>>n;
	for(i=0;i<n;i++){
     
		cin>>x>>y;
		s.insert(make_pair(x,y));
	}
	n = s.size();
	for(i=0,it=s.begin(),it++;it!=s.end();it++,i++){
     
		A[i]=(*it).first;
		B[i]=(*it).second;
	}
	long long ans=2;
	for(i=1;i<n;i++){
     
		set<pair<double,double> > se;
		for(j=i-1;j>=0;j--){
     
			double x=(B[j]-B[i])/(A[i]-A[j]);
			double y=(A[j]*B[i]-A[i]*B[j])/(A[j]-A[i]);
			se.insert(make_pair(x,y));
		}
		int n2=se.size();
		ans+=(n2+1);
	}
	cout<<ans<<endl;
} 

试题 J 字串排序

小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。
小蓝找到了很多字符串试图排序,他恰巧碰到一个字符串,需要 V 次交换,可是他忘了把这个字符串记下来,现在找不到了。
请帮助小蓝找一个只包含小写英文字母且没有字母重复出现的字符串,对该串的字符排序,正好需要 V 次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。

【输入格式】 输入的第一行包含一个整数V,小蓝的幸运数字。

【输出格式】 题面要求的一行字符串。

【样例输入】

4

【样例输出】

bbaa

【样例输入】

100

【样例输出】

jihgfeeddccbbaa
#include 
using namespace std;
const int N = (int)1e4+5;

int num[N], res[N];
int n, m, _max, len;

bool judge(int letter) {
     
	int i = 26, j = letter;
	while (!res[i]) i--;
	if (i == j) {
     
		while (i > 0 && j > 0) {
     
			if (res[i] != num[j]) {
     
				return res[i] > num[j];
			} else {
     
				i--; j--;
			}
		}
	}
	return i > j;
}

void dfs(int letter, int curlen, int cursum, int l) {
     
	if (cursum > n) return ;
	if (letter > _max) return ;
	if (curlen > len) return ;
	if (curlen == len && cursum != n) return ;
	if (letter == _max && cursum != n) return ;
	
	if (cursum == n) {
     
		if (curlen < len || judge(letter)) {
      //长度减小或字典序减小更新结果
			len = curlen;
			for (int i = 1; i <= 26; i++) {
     
				res[i] = num[i];
			}
		}
		return ;
	}
	for (int i = 1; i <= l; i++) {
     
		num[letter + 1] = i;
		dfs(letter + 1, curlen + i, cursum + i * curlen, i);
	}
	num[letter + 1] = 0;
}

int main() {
     
	scanf("%d", &n);
	m = 0; len = 0;
	while (m < n) {
     
		int id = 1;
		for (int i = 2; i <= 26; i++) {
      //找到s最小的点, 如果存在多个取字典序更小的
      if (res[i] < res[id]) id = i;
		}
		m += len - res[id];
		_max = max(_max, id);
		len ++; res[id] ++;
	}
	dfs(0, 0, 0, 10);
	for (int i = _max; i >= 1; i--) {
     
		for (int j = res[i]; j > 0; j--) {
     
			printf("%c", i-1+'a');
		}
	}
	printf("\n");
  return 0;
}

你可能感兴趣的:(蓝桥杯,算法基础,c++)