2019第十届蓝桥杯【c/c++】B组省赛 题解+感想(持续更新)

先介绍一下我的个人背景:本人大一,二本院校普通学生一枚。暑假看视频学完c语言,去年9月到今年3月利用空闲时间学了递归回溯,dfs, bfs, 快排归并, dp和贪心基础,数据结构的链表,栈和队列(因为bfs不得不学),进制转换,大数加减,欧几里得,素数筛等等。刷的题不是很多,经常一个晚上只能做一道题,资质一般。应该能代表大部分比较努力学习算法,不想荒废大学时光的大学生了吧。

参赛感想:考试前一周做了前两届蓝桥杯的省赛题目,感觉压力比较大,题意都看不懂。。。于是带着一个起伏不定的心理进了赛场。但总体感觉这一届蓝桥杯题目比历届水一点,题意很明确,题目没有历届灵活,很多题一看就知道怎么做。不过第一次参加,患得患失还是比较严重,前两道就花了一个小时仔细找坑。。这次做了8道,二叉树和最后一道没做,目前已知的错了两道,应该无缘国赛了吧。。 ok,下面上题目。

试题A:

在这里插入图片描述
在这里插入图片描述

解析:

题意就是从以下队员挑选到1-5号位,每个队员只能选择一次。因此只需要选出每一列排前三的分数,自己再组合一下,找到最优解就行。17号贼强,1,3,4号位都是第一,但17号只能选择一次。知道题意后就是小学奥数题,手算就行。如果写代码,就是dfs暴力枚举了。
答案:490(有多组解)

试题B:

在这里插入图片描述

解析:

26进制,可以计算器算,也可以直接拉excal表(上面的字母排列就是26进制)
在这里插入图片描述
答案:BYQ

试题C:

在这里插入图片描述

解析:

第一反应是变形的斐波那契数列大数据,大整数加法。不过要求到第千万项,用大数加法时间复杂度太大。我列了前50项,用后四位加了一下测试了几个例子,发现其实就是计算后四位就行了。数据太大,不能用数组存,用几个变量迭代下去就行。直接上代码吧。

#include <cstdio>
using namespace std;

int main()
{
	int a = 1, b = 1, c = 1, tmp;
	for(int i = 4;i < 20190350;i++)
	{
		tmp = c;
		c = a + b + c;
		c %= 10000;
		a = b;
		b = tmp;
		if(i > 20190300) printf("%d - %d\n",i, c);
	}
	return 0;
}

在这里插入图片描述
答案:4659

试题D:

在这里插入图片描述

解析:

请记住:正整数不包括0
其实只需要两层循环枚举a和b,然后算出c,判断c是否是正整数并且不包含2,4就行。最后枚举的计数除以6就是答案。
为什么除以6呢?
在这里插入图片描述
首先,题目条件是a,b,c各不相同,而我们是将a,b,c所有的排列都枚举出来。拿上面1 3 8举例,重复出现了6次。这个6就是全排列的个数3!

#include <iostream>
using namespace std;

int is(int x)
{
	while(x)
	{
		if(x%10 == 2 || x%10 == 4) return 1;
		x /= 10;
	}
	return 0;	//包含2,4 
}

int main()
{
	const int n = 2019;
	int a, b, c, ans = 0;
	for(a = 1;a <= n;a++)
	{
		if(is(a) && a != 0) continue;
		for(b = 1;b <= n;b++)
		{
			if(is(b) && b != 0) continue;
			c = n-a-b;
			if(is(c) && c != 0) continue;
			if(a == b || b == c || a == c || c <= 0) continue;
		//	cout<<a<<" "<<b<<" "<<c<<endl;
			ans++;
		}
	}
	cout<<ans/6<<endl;
	return 0;
}

答案:40785

试题E:

在这里插入图片描述
在这里插入图片描述

解析:

其实就是一道典型的bfs问题,字典序的条件只需要调整一下遍历的优先方向就行。比赛的时候因为迷宫数据太大,输出路径的时候程序总是闪退,优化了1个小时? 由于暂时没有文档,因此只能演示一下样例。
在这里插入图片描述

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

char a[32][52];

typedef struct 
{
	int x;
	int y;
	int pre;		//存放父结点的编号 
	char turn;
}node;

typedef struct 
{
	node data[1000];	//存放结点的队列 
	int front;		//队列头部 
	int rear;		//队列尾部 
}Queue;

void print(Queue qu, int cur)	//获取该队列和当前结点的参数,由于要倒序输出,因此用递归(调用栈) 
{
	while(qu.data[cur].pre != -1)
	{
		printf("%c",qu.data[cur].turn);
		cur = qu.data[cur].pre;
	}
}

void path(int xi, int yi, int xe, int ye)	//从(xi,yi)走到(xe,ye)
{
	int ok = 0; 
	Queue qu;	//创建队列
	qu.rear = 0;	//初始化队列
	qu.front = -1;
	qu.data[0].x = 0;
	qu.data[0].y = 0;
	qu.data[0].pre = -1; 	//初始结点的pre设为-1,作为输出路径的递归出口 
	qu.data[0].turn = '*'; 
	a[xi][yi] = '2';	//表示该结点已经遍历过 
	while(qu.front != qu.rear && ok == 0)	//若队列不为空 (队列为空表示所有能扩展的结点都扩展完了也没能发现目标结点,表示无解) 
	{
		qu.front++;		//出队,front指向出队结点 
		if(qu.data[qu.front].x == xe && qu.data[qu.front].y == ye)	//若出队的结点是目标结点
		{
			ok = 1;
			print(qu, qu.front);		//根据该结点反推到初始结点并输出
		} 
		else
		{
			int k;
			char t;
			int i = qu.data[qu.front].x, j = qu.data[qu.front].y;				 
			for(k = 0;k < 4;k++)	//循环4次,扩展该出队结点 
			{
				i = qu.data[qu.front].x, j = qu.data[qu.front].y;
				switch(k)
				{
					case 0: i++;t='D'; break;
					case 1: j--;t='L'; break;
					case 2: j++;t='R'; break;
					case 3: i--;t='U'; break;
				}
				if(a[i][j] == '0' && i >= 0 && i <= xe && j >= 0 && j <= ye)	//扩展结点可用  
				{
				//	printf("%d\n",k);
					qu.rear++;		//将扩展的结点进队并初始化进队结点的坐标和并将pre设为父结点的编号(之后可通过pre找到父结点) 
					qu.data[qu.rear].x = i, qu.data[qu.rear].y = j;	 
					qu.data[qu.rear].pre = qu.front;
					qu.data[qu.rear].turn = t;	
					a[i][j] = '2'; //表示该结点已经遍历过,避免后面重复遍历
				} 
			} 
		} 
	} 
	if(ok == 0) printf("无解。\n");
} 

int main()
{
	int i, j, n = 30, m = 50;			//初始化迷宫 
	memset(a, '*',sizeof(a));

	for(i = 0;i < n;i++)
	{
		for(j = 0;j < m;j++)
		scanf("%c",&a[i][j]);
		getchar();
	}

	path(0,0,n-1,m-1);
	return 0; 
}

试题F:

在这里插入图片描述

解析:

挺简单的一道分离数字的题,不知道有什么坑。。。(不管,样例过了就是过了)

#include <cstdio>
using namespace std;

int is(int x)
{
	while(x)
	{
		if(x%10 == 2 || x%10 == 0 || x%10 == 1 || x%10 == 9) return 1;
		x /= 10;
	}
	return 0;
}

int main()
{
	int i, n;
	long long ans = 0;
	scanf("%d",&n);
	for(i = 1;i <= n;i++)
	{
		if(is(i)) ans += i;
	}
	printf("%lld\n",ans);
	return 0;
}

试题G:

在这里插入图片描述
在这里插入图片描述
就对每一层加起来就行。

#include <iostream>
using namespace std;

int main()
{
	int n;
	int arr[100010];
	scanf("%d",&n);
	for(int i = 0;i < n;i++) scanf("%d",&arr[i]);
	long long h = 1, cnt = 1, num = 1, sum = 0, maxsum = arr[0], ans = 1;
	while(num < n)
	{
		cnt *= 2;
		h++;
		num += cnt;
		sum = 0;
		for(int i = 0;i < cnt;i++) sum += arr[i+cnt-1];
		if(sum > maxsum) ans = h, maxsum = sum;
	}
	printf("%lld\n",ans);
	return 0;
} 

试题H:

在这里插入图片描述
在这里插入图片描述

解析:

读懂题意后第一反应就是找公约数。找每个数之间的最大公约数(求最短项),在每个公约数里面找公约数最小的(包括给出的所有整数),gcd,欧几里得嘛。之后将总项算出来就行。不过可能会有些坑是我没注意的。上代码:

#include <cstdio>
#include <algorithm>
using namespace std;

int gcd(int a, int b)
{
	if(b == 0) return a;
	return gcd(b, a%b);
} 

int main()
{
	int i, n, max_num = -1, min_num, min_gcd;
	int a[100010], ans = 0, tmp, flag = 0;
	scanf("%d",&n);
	for(i = 0;i < n;i++)
	{
		scanf("%d",&a[i]);
		if(i == 0)
		{
			max_num = a[0];
			min_num = a[0];
		}
		else
		{
			if(a[i] == a[i-1]) flag = 1;
			tmp = gcd(a[i], a[i-1]);
			if(min_gcd > tmp) min_gcd = tmp;
			max_num = max(a[i], max_num);
			min_num = min(a[i], min_num);
		}
	}
	if(flag) ans = n;
	else	ans = (max_num-min_num)/min_gcd + 1;
	printf("%d\n",ans);
	return 0;
}

**更新:**上面这个代码是错的,当A[i]取0就不行了。其实思路就是求这n个数的最大公约数,ans = (Max-Min)/g + 1.

#include <iostream>
using namespace std;

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

int main()
{
	int n;
	long long arr[100010], Min, Max, d, ans;
	scanf("%d",&n);
	for(int i = 0;i < n;i++)
	{
		scanf("%lld",&arr[i]); 
		if(i == 0)
		{
			Min = arr[0], Max = arr[0], d = arr[0];
		}
		else
		{
			d = gcd(d,arr[i]);
			Min = min(Min,arr[i]);
			Max = max(Max,arr[i]);
		}
	}
	ans = (Max-Min)/d+1; 
	printf("%lld\n",ans);
	return 0; 
}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值