第十一届蓝桥杯校内模拟赛
题目链接
蓝桥杯官网 辅导资料
往下翻,这个就是了。
填空题
1. 15.125GB
【解题思路】
bit是位,B是字节,1B = 8bit,除此之外任意两个都是1024的距离。
所以手机的4GB内存就是
4
∗
2
30
=
2
32
4*2^{30} = 2^{32}
4∗230=232个字节(B)
【代码】
#include<iostream>
using namespace std;
int main(){
int res = 15.125*1024;
printf("%d", res);
return 0;
}
2. 约数个数
【解题思路】
填空题怎么方便怎么来,枚举。
【代码】
#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=n0+n1+n2
- n 0 = n 2 + 1 n0 = n2+1 n0=n2+1
有: n 0 = ( n − n 1 + 1 ) / 2 n0 = (n-n1+1)/2 n0=(n−n1+1)/2
要使n0最大,n1要最小,则n1 = 0
所以: n 0 = ( n + 1 ) / 2 n0 = (n+1)/2 n0=(n+1)/2
【代码】
#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,然后从第二个字母开始,如果:
- 上一个字母为辅音(last = false):
- 当前字母也为辅音,表明处于同一段,什么也不做
- 当前字母为元音,表明进入了下一段,idx++,last = true
- 上一个字母为辅音(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)
f(i,j)=f(i,j−1)+1+f(j,abs(i−j)−1)
就是一次减小一层规模,展开一层的感觉,第一项是剩余的第一个数是i,第二个是1~j-1的序列个数,第二项是展开的那一层,也就是第一个数是i,第二个数是j的序列个数,那么有两种:
- 只有两个数的序列(i,j)个数,当然只有一个
- 大于两个数的序列,即第一个数是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
p1=pm+1,p2=p2+1 直到
p
1
=
=
p
2
p1 == p2
p1==p2, 或者
p
2
=
=
n
p2 == n
p2==n
p
1
=
=
p
2
p1 == p2
p1==p2的情况说明区间内就一个数,直接输出数组后的全部数就行了,也就是节目都得上,因为初始时p2的取值就使得后面的数只有m-1个了。
还是这个栗子:
6 3
3 4 8 7 1 2
- 初始时: p 1 = 0 , p 2 = 3 p1 = 0, p2 = 3 p1=0,p2=3(这样才能保证剩余的区间还能取到2个数), 区间为 [3 4 8 7], 遍历找到 p m = 2 , M A X = 8 pm = 2, MAX = 8 pm=2,MAX=8,输出第一个节目的好看值8;
- 更新 p 1 = 3 , p 2 = 4 p1 = 3, p2 = 4 p1=3,p2=4, 区间为[7, 1], 遍历找到 p m = 3 , M A X = 7 pm = 3, MAX = 7 pm=3,MAX=7,输出第二个节目的好看值7;
- 更新 p 1 = 4 , p 2 = 5 p1 = 4, p2 = 5 p1=4,p2=5, 区间为[1, 2], 遍历找到 p m = 5 , M A X = 2 pm = 5, MAX = 2 pm=5,MAX=2,输出2;
- 更新 p 1 = 6 , p 2 = 6 , p 2 = = n p1 = 6, p2 = 6, p2 == n p1=6,p2=6,p2==n, 退出。
最终得到了8、7、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;
}
心得体会
感觉蓝桥杯很爱考数学问题。