第十一届蓝桥杯校内模拟赛(我的总结)


直接去看视频吧,传送门

题目连接

蓝桥杯官网 辅导资料
往下翻,这个就是了。
在这里插入图片描述html

填空题

1. 15.125GB

【解题思路】
单位换算
bit是位,B是字节,1B = 8bit,除此以外任意两个都是1024的距离。
在这里插入图片描述
因此手机的4GB内存就是 4 2 30 = 2 32 4*2^{30} = 2^{32} 个字节(B)node

【代码】ios

#include<iostream>
using namespace std;
int main(){
	int res = 15.125*1024;
	printf("%d", res);
	return 0;
}

2. 约数个数

【解题思路】
填空题怎么方便怎么来,枚举。
【代码】web

#include<iostream>
using namespace std;
int main(){
	int n = 1200000;
	int cnt = 0;
	for(int i=1; i<=n; i++){
		if(n%i == 0){
			cnt++;
		}
	}
	printf("%d", cnt);
	return 0;
}

3. 叶结点数

【解题思路】编程

  • n = n 0 + n 1 + n 2 n = n0+n1+n2
  • n 0 = n 2 + 1 n0 = n2+1

有: n 0 = ( n n 1 + 1 ) / 2 n0 = (n-n1+1)/2 数组

要使n0最大,n1要最小,则n1 = 0数据结构

因此: n 0 = ( n + 1 ) / 2 n0 = (n+1)/2 app

【代码】svg

#include<iostream>
using namespace std;
int main(){
	int n = 2019;
	printf("%d", (n+1)/2); 
	return 0;
}

4. 数字9

【解题思路】
枚举1~2019,取每一个数的每一位判断是否为9,遇到九就不用继续判断剩余的位数了。学习

【代码】

#include<iostream>
using namespace std;
bool hasEight(int n){
	bool flag = false;
	while(n>0){
		if(n%10 == 9){
			flag = true;
			break;
		}
		n /= 10;
	}
	return flag;
}
int main(){
	int n = 2019, ans = 0;
	for(int i=1; i<=2019; i++){
		if(hasEight(i))
			ans++;
	}
	printf("%d", ans);
	return 0;
}

编程题

5. 数位递增的数

【解题思路】
最大数只有7位,n最大1000000(10^6),每个数枚举位数判断复杂度是O(1)的,n个数O(7n),能够枚举。

【代码】

#include<iostream>
using namespace std;
bool ok(int n){
	if(n < 10)
		return true;
	bool flag = true;
	int x = n%10;
	n /= 10;
	while(n>0){
		if(n%10 > x){
			flag = false;
			break;
		}
		//更新x和n 
		x = n % 10;
		n /= 10;
	}
	return flag;
}
int main(){
	int n;
	scanf("%d", &n);
	int ans = 0;
	for(int i=1; i<=n; i++){
		if(ok(i)){
			ans++;
		}
	} 
	printf("%d", ans);
	return 0;
}

6. 递增三元组

【解题思路】
枚举每个数,看左边有没有比它小的 && 右边有没有比它大的,时间复杂度O(n^2),不会超时。

【代码】

#include<iostream>
using namespace std;
const int maxn = 10010;
int num[maxn];
int main(){
	int n;
	scanf("%d", &n);
	for(int i=0; i<n; i++){
		scanf("%d", &num[i]);
	} 
	int ans = 0, x;
	for(int i=1; i<n-1; i++){
		x = num[i];
		bool hasSmall = false, hasBig = false;
		for(int j=0; j<i; j++){
			if(num[j] < x){
				hasSmall = true;
				break;
			}
		}
		for(int j=i+1; j<n; j++){
			if(num[j] > x){
				hasBig = true;
				break;
			}
		}
		if(hasSmall && hasBig){
			ans++;
		}
	}
	printf("%d", ans);
	return 0;
}

7. 音节判断(扫描一遍字符串)

【解题思路】
由于要能把单词划分为辅音元音辅音元音四个部分,只要从头至尾扫描一边字符串便可,时间复杂度为O(n),n为字符串长度。

我是这样作的,设置一个标识last表示当前字母的前一个字母是不是元音,初始值为输入字符串的第一个字母是否为元音,若是是,last = true,这是程序能够直接退出了,由于第一部分必须是辅音。
设置idx,代表当前字母处于第几部分。

扫描过程:
若是第一个字母是辅音,last = false,而后从第二个字母开始,若是:

  1. 上一个字母为辅音(last = false):
  • 当前字母也为辅音,代表处于同一段,什么也不作
  • 当前字母为元音,代表进入了下一段,idx++,last = true
  1. 上一个字母为辅音(last = true):
  • 当前字母也为元音,代表处于同一段,什么也不作
  • 当前字母为辅音,代表进入了下一段,idx++,last = false

最后若是idx为4代表恰好符合要求的四段,输出yes,反之输出no

【例如】 lanqiao

  • 初始值idx = 1, last = false(l为辅音)
  • idx = 2, last = true(a为元音)
  • idx = 3, last = false(n为辅音)
  • nothing to do(q仍是辅音,啥都不变)
  • idx = 4, last = true(i为元音)
  • nothing to do(a仍是元音,啥都不变)
  • nothing to do(0仍是元音,啥都不变)
  • 最后idx = 4,输出yes

【代码】

#include<iostream>
#include<cstring>
using namespace std;
string vowel = "aeiou";
bool isVowel(char ch){
	bool flag = false;
	for(int i=0; i<vowel.length(); i++){
		if(vowel[i] == ch){
			flag = true;
			break;
		}
	}
	return flag;
}
int main(){
	string str; 
	cin>>str;
	bool last = false;//前一个字母是元音为true 
	if(isVowel(str[0])) last = true; 
	//辅音元音辅音元音
	int idx = 1;
	if(last){
		printf("no");
	} 
	else{
		for(int i=1; i<str.length(); i++){
			if(last){
				//若是前一个是元音而且当前字母也是元音,什么都不作
				//若是当前字母是辅音,idx++; 
				if(!isVowel(str[i])){
					idx++;
					last = false;
				} 
			}
			else{
				//前一个是辅音
				//当前是元音,idx++
				if(isVowel(str[i])){
					idx++;
					last = true; 
				}
			}
		}
		if(idx == 4){
			printf("yes");
		}
		else{
			printf("no");
		}
	}
	return 0;
}

8. 长草

【解题思路】
这是一个典型的BFS题目,总共有nm个位置,习惯说结点,那么最多每一个结点都入队一次,时间复杂度为O(nm)

结点用结构体表示,除告终点的坐标,还须要记录该结点是第几个月长出来的,也就是层数。

struct node{
	int x, y;
	int layer;
};

用到的变量有:

  • 设置了bool型的matrix数组表示每一个位置上是否有草;
  • bool型的inq数组指示每一个结点是否已经入过队了;
  • 还有题目给的n、m、k;
  • 已经为了实现宽度优先搜索的数据结构queue q

上述都放在全局,在竞赛时这样很方便,不会涉及到引用之类的,没啥坏处,因此能放在全局我就不会传参。

读入数据时,若是是“g”就将matrix[i][j]设为true,把该结点入队(注意层数设置为0)并设置为已入队,而后进行BFS。

BFS时取出队首元素,若是该元素的layer == k,就直接退出了(只看第k月草的长势状况),不然枚举它周围的结点,并将层数设置为layer+1,而后若是没入过队而且没出界,就加入队列。
注意不须要管该地如今有没有草,都须要入队。

【代码】

#include<iostream>
#include<queue>
using namespace std;
const int maxn = 1010;
struct node{
	int x, y;
	int layer;
};
//增量矩阵
int X[4] = {0, 0, 1, -1};
int Y[4] = {1, -1, 0, 0}; 
bool matrix[maxn][maxn];//matrix[i][j] = true表示(i, j)处有草
bool inq[maxn][maxn] = {false};//检查坐标为(i, j)的点是否已经入过队列
queue<node> q;
int n, m, k;
bool judge(int i, int j){
	//越界 
	if(i<0||i>=n||j<0||j>=m) return false;
	//入过队 
	if(inq[i][j]) return false;
	return true;
}
void BFS(){
	while(!q.empty()){
		node top = q.front();
		q.pop();
		if(top.layer == k){
			break;
		}
		node temp;
		temp.layer = top.layer + 1; //层数为父节点加1 
		//枚举四个方向 
		for(int i=0; i<4; i++){
			temp.x = top.x + X[i];
			temp.y = top.y + Y[i];
			if(judge(temp.x, temp.y)){
				matrix[temp.x][temp.y] = true;
				inq[temp.x][temp.y] = true;
				q.push(temp);
			}
		}
	}
}
int main(){
	scanf("%d%d", &n, &m);
	getchar();
	char ch;
	node temp;
	for(int i=0; i<n; i++){
		for(int j=0; j<m; j++){
			scanf("%c", &ch);
			if(ch == '.') matrix[i][j] = false;
			else{
				matrix[i][j] = true;
				temp.x = i;
				temp.y = j;
				temp.layer = 0;//一开始就种了,至关于第0个月的状况
				q.push(temp); 
				inq[i][j] = true;
			}
		}
		getchar();//一行读完了记得读掉空行 
	}
	scanf("%d", &k);
	BFS();
	for(int i=0; i<n; i++){
		for(int j=0; j<m; j++){
			if(matrix[i][j])
				printf("g");
			else
				printf(".");
		}
		printf("\n");
	}
	return 0;
}

9. 序列计数

【解题思路】
蓝桥杯总会考一题相似这样的搜索问题。题意清楚可是又根本感受无从下手。这种时候要尽力想办法一层一层剥开来考虑,首先想一个能表达出结果的方法(我以为有点像动态规划找表达式的感受),而后想办法怎么才能让这个表达规模变小(好像又有点分治的意思),直到能求出答案。

因此用f(i, j)表示第一个数是i,第二个数是1~j的序列个数,结果能够表达为f[n, n),那表达式是: f ( i , j ) = f ( i , j 1 ) + 1 + f ( j , a b s ( i j ) 1 ) f(i, j) = f(i, j-1)+1+f(j, abs(i-j)-1)
就是一次减少一层规模,展开一层的感受,第一项是剩余的第一个数是i,第二个是1~j-1的序列个数,第二项是展开的那一层,也就是第一个数是i,第二个数是j的序列个数,那么有两种:

  1. 只有两个数的序列(i,j)个数,固然只有一个
  2. 大于两个数的序列,即第一个数是i,第二个数是j, 第三个是1~abs(i-j)-1的序列个数

【代码】

#include<iostream>
using namespace std;
const int maxn = 1010;
const int MOD = 10000;
int mem[maxn][maxn]; 
int n;
int dfs(int i, int j){
	//f(i,j)表示前一个数是i,当前数是1到j的合法序列的个数
	if(j <= 0) return 0;
	if(mem[i][j] != 0) return mem[i][j];
	//展开一层(展开前一个数是i,当前数是j的合法序列这层,个数为 1 + dfs(i, abs(i - j) - 1)) 
	else return mem[i][j] = (dfs(i, j-1) + 1 + dfs(j, abs(i-j)-1))%MOD;
}
int main(){
	scanf("%d", &n);
	int res = dfs(n, n);
	printf("%d", res);
	return 0;
}

10. 晚会节目单

【解题思路】
一看题:直接结构体两次排序,这么简单啊。
果真是我想多了…

果真看懂题目才是最重要的,题目表达的是:他但愿选出的第一个节目尽量好看,在此前提下但愿第二个节目尽量好看,依次类推。
并非找前m大的数按照输入顺序输出,而是找字典序最大的m个数组成的序列。


举个栗子:
6 3
3 4 8 7 1 2

若是只是找前m大的数,那么(结构体先按好看值排序再按索引排序)会输出:
4 8 7
但题目的意思须要输出:
8 7 2


由于即便第一个序列的总好看值更大,但第二个序列才知足了第一个节目尽量好看,也就是字典序大的要求。

那么可使用two pointers来解决(找的是区间最值):
两个指针p1, p2分别指向区间的首尾,初始时p1 = 0, p2 = n-m, 由于第一个数只能在这个区间内,若是p2再大,就不能保证总共能取到m个数。

遍历[p1, p2]范围内的好看值数组,用pm指示区间内最大元素的下标,MAX指示区间内最大的元素(记录好找最大值),输出MAX。

更新区间 p 1 = p m + 1 , p 2 = p 2 + 1 p1 = pm + 1, p2 = p2 + 1 直到 p 1 = = p 2 p1 == p2 , 或者 p 2 = = n p2 == n
p 1 = = p 2 p1 == p2 的状况说明区间内就一个数,直接输出数组后的所有数就好了,也就是节目都得上,由于初始时p2的取值就使得后面的数只有m-1个了。


仍是这个栗子:
6 3
3 4 8 7 1 2

  • 初始时: p 1 = 0 , p 2 = 3 p1 = 0, p2 = 3 (这样才能保证剩余的区间还能取到2个数), 区间为 [3 4 8 7], 遍历找到 p m = 2 , M A X = 8 pm = 2, MAX = 8 ,输出第一个节目的好看值8;
  • 更新 p 1 = 3 , p 2 = 4 p1 = 3, p2 = 4 , 区间为[7, 1], 遍历找到 p m = 3 , M A X = 7 pm = 3, MAX = 7 ,输出第二个节目的好看值8;
  • 更新 p 1 = 4 , p 2 = 5 p1 = 4, p2 = 5 , 区间为[1, 2], 遍历找到 p m = 5 , M A X = 2 pm = 5, MAX = 2 ,输出2;
  • 更新 p 1 = 6 , p 2 = 6 , p 2 = = n p1 = 6, p2 = 6, p2 == n , 退出。

最终获得了八、七、2,这是六个数中取三个数的序列中字典序最大的一个。


【代码】

复杂度是O(n^2),会超时,我这就去学习ST和线段树。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int show[maxn];
int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	for(int i=0; i<n; i++){
		scanf("%d", &show[i]);
	}
	//在n个数中找出字典序最大的m个数的序列 
	int p1 = 0, p2 = n-m, pm;
	while(p1 < p2 && p2 < n){
		int MAX = -1;
		for(int i=p1; i<=p2; i++){
			if(show[i] > MAX){
				MAX = show[i];
				pm = i;	
			}	
		}
		printf("%d ", show[pm]);
		//更新区间 
		p1 = pm+1;
		p2++;
	} 
	//p1和p2相等,说明后面的节目都被选中了 
	if(p2 < n){
		for(int i=p2; i<n; i++){
			printf("%d ", show[i]);
		} 
	}
	return 0;
}

心得体会

感受蓝桥杯很爱考数学问题。