目录
2.约数个数
3.叶结点数
4.数字9
7.音节判断
8.长草
9.序列计数
第十一届蓝桥杯大赛个人赛校内选拔(软件类)题目
第十一届蓝桥杯大赛个人赛校内选拔(软件类)题解
1.15.125GB
【问题描述】
在计算机存储中,15.125GB是多少MB?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
15488
2.约数个数
【问题描述】
1200000有多少个约数(只计算正约数)。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
public class _11_02 {
public static void main(String[] args) {
int n = core(1200000);
System.out.println(n);
}
public static int core(int f) {
int n = 0;
for(int i=1;i<=f;i++) {
if(f%i==0) ++n;
}
return n;
}
}
3.叶结点数
【问题描述】
一棵包含有2019个结点的二叉树,最多包含多少个叶结点?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
方法一
找规律
用画图工具把二叉树画出来
将结点数和二叉树分别列出来后
1 1
3 2
4 3
5 3
6 3
7 4
8 4
9 5
10 5
11 6
12 6
13 7
14 7
15 8
16 8
不难发现
当结点数为偶数时,叶子结点数等于结点数的一半
当结点数为奇数时,叶子结点数等于结点数的一半(取整后)加1
2019为奇数,故答案为
2019/2 + 1 = 1010
方法二
给出固定结点数,求二叉树的叶子结点的最大值
问题可转换为已知结点数求满二叉树或者完全二叉树
根据满二叉树的性质,设二叉树的深度为 n
第 n 层有 2^(n-1) 个结点,二叉树一共有 (2^n)-1 个结点
public class _11_03 {
public static void main(String[] args) {
int result = core(2019);
System.out.println(result);
}
/**
* 已知叶二叉树的结点数,求最大叶子结点数
*/
public static int core(int f) {
if(f<2) return f;
// f表示二叉树的结点数
int n = 0; // n表示二叉树的高度
int an = 0; // an表示第n层完全二叉树的叶子结点数
int m = 0; // an表示n层完全二叉树的结点数
while (f > m) {
an = (int) Math.pow(2, n - 1);
m = (int) Math.pow(2, n) - 1;
// System.out.println("第 " + n + " 层有 " + m + " 个结点, " + an + " 个叶子结点, " + (m - an) + " 个非叶子结点");
++n;
} --n;
int b = f-(m-an); // 当结点数为f时,最底层的叶子结点数
int b0 = b%2==0 ? b/2 : b/2+1;
int c = (int) Math.pow(2, n - 1 -1) - b0; // 倒数第二层的叶子结点数 = 倒数第二层结点数 - 底层叶子结点数的上层结点数
System.out.println("拥有 "+f+" 个结点的二叉树,有 "+b+" 个叶子结点在第 "+n+" 层,"+c+" 个叶子结点在第 "+(n-1)+" 层");
int result = b + c;
return result;
}
}
运行结果
第 0 层有 0 个结点, 0 个叶子结点, 0 个非叶子结点
第 1 层有 1 个结点, 1 个叶子结点, 0 个非叶子结点
第 2 层有 3 个结点, 2 个叶子结点, 1 个非叶子结点
第 3 层有 7 个结点, 4 个叶子结点, 3 个非叶子结点
第 4 层有 15 个结点, 8 个叶子结点, 7 个非叶子结点
第 5 层有 31 个结点, 16 个叶子结点, 15 个非叶子结点
第 6 层有 63 个结点, 32 个叶子结点, 31 个非叶子结点
第 7 层有 127 个结点, 64 个叶子结点, 63 个非叶子结点
第 8 层有 255 个结点, 128 个叶子结点, 127 个非叶子结点
第 9 层有 511 个结点, 256 个叶子结点, 255 个非叶子结点
第 10 层有 1023 个结点, 512 个叶子结点, 511 个非叶子结点
第 11 层有 2047 个结点, 1024 个叶子结点, 1023 个非叶子结点
拥有 2019 个结点的二叉树,有 996 个叶子结点在第 11 层,14 个叶子结点在第 10 层
1010
图形分析
假设二叉树是完全二叉树,有最后一层的结点数为1024,倒数第二层结点数为512
由此可见,2019个结点的二叉树在最底层的叶子节点数为2048-2019=996
则有倒数第二层的叶子结点数等于此层结点数减去底层叶子结点数的父节点数512-498=14
最终结果
996+14=1010
4.数字9
【问题描述】
在1至2019中,有多少个数的数位中包含数字9?
注意,有的数中的数位中包含多个9,这个数只算一次。例如,1999这个数包含数字9,在计算只是算一个数。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
public class _11_04 {
public static void main(String[] args) {
int n = core(2019);
System.out.println(n);
}
public static int core(int f) {
int n = 0;
for(int i=1;i<=f;i++) {
if(judge(i)) {
++n;
}
}
return n;
}
public static boolean judge(int n) {
while(n > 0) {
if(n%10 == 9) return true;
n/=10;
}
return false;
}
}
最终结果
544
5.数位递增的数
【问题描述】
一个正整数如果任何一个数位不大于右边相邻的数位,则称为一个数位递增的数,例如1135是一个数位递增的数,而1024不是一个数位递增的数。
给定正整数 n,请问在整数 1 至 n 中有多少个数位递增的数?
【输入格式】
输入的第一行包含一个整数 n。
【输出格式】
输出一行包含一个整数,表示答案。
【样例输入】
30
【样例输出】
26
【评测用例规模与约定】
对于 40% 的评测用例,1 <= n <= 1000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000。
import java.util.Scanner;
/**
* 5.数位递增的数
*/
public class _11_05 {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
public static void main(String[] args) {
int result = core(n);
System.out.println(result);
}
public static int core(int n) {
int result = 0;
for(int i=1;i<=n;++i) {
if(judge(i)) {
++result;
}
} return result;
}
/**
* 判断是否数位递增
*/
public static boolean judge(int n) {
if(n < 10) return true;
int digit = 0; // n的位数
int m = n;
while(m/10 > 0) {
m/=10;
++digit;
} ++digit;
// System.out.println("digit "+digit);
int left = 0, right = 10;
while(digit-- > 0) {
left = n%10;
if(left > right) return false;
right = left;
n/=10;
}
return true;
}
}
运行结果
300
135
6.递增三元组
【问题描述】
在数列 a[1], a[2], ..., a[n] 中,如果对于下标 i, j, k 满足 0<i<j<k<n+1 且 a[i]<a[j]<a[k],则称 a[i], a[j], a[k] 为一组递增三元组,a[j]为递增三元组的中心。
给定一个数列,请问数列中有多少个元素可能是递增三元组的中心。
【输入格式】
输入的第一行包含一个整数 n。
第二行包含 n 个整数 a[1], a[2], ..., a[n],相邻的整数间用空格分隔,表示给定的数列。
【输出格式】
输出一行包含一个整数,表示答案。
【样例输入】
5
1 2 5 3 5
【样例输出】
2
【样例说明】
a[2] 和 a[4] 可能是三元组的中心。
【评测用例规模与约定】
对于 50% 的评测用例,2 <= n <= 100,0 <= 数列中的数 <= 1000。
对于所有评测用例,2 <= n <= 1000,0 <= 数列中的数 <= 10000。
import java.util.Arrays;
import java.util.Scanner;
/**
* 6.递增三元组
*/
public class _11_06 {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
static int[] A = new int[n];
static {
for (int i=0;i<n;i++) {
A[i] = sc.nextInt();
}
}
public static void main(String[] args) {
System.out.println(Arrays.toString(A));
int result = core(A);
System.out.println(result);
}
public static int core(int[] A) {
if(A.length<=2) return 0;
int max = 0;
for (int i=0;i<A.length-2;i++) {
int result = 0;
for(int j=i+1;j<A.length-1;j++) {
for(int k=j+1;k<A.length;k++) {
if(A[i]<A[j] && A[j]<A[k]) {
++result; break;
}
}
}
max = result > max ? result : max;
}
return max;
}
}
运行结果
16
1 2 5 3 5 0 1 2 1 2 1 2 1 2 1 2
5
7.音节判断
【问题描述】
小明对类似于 hello 这种单词非常感兴趣,这种单词可以正好分为四段,第一段由一个或多个辅音字母组成,第二段由一个或多个元音字母组成,第三段由一个或多个辅音字母组成,第四段由一个或多个元音字母组成。
给定一个单词,请判断这个单词是否也是这种单词,如果是请输出yes,否则请输出no。
元音字母包括 a, e, i, o, u,共五个,其他均为辅音字母。
【输入格式】
输入一行,包含一个单词,单词中只包含小写英文字母。
【输出格式】
输出答案,或者为yes,或者为no。
【样例输入】
lanqiao
【样例输出】
yes
【样例输入】
world
【样例输出】
no
【评测用例规模与约定】
对于所有评测用例,单词中的字母个数不超过100。
状态标记法
将四个阶段的每个状态用数字a1-a4表示出来
初始状态为0,进入状态则为1,状态结束为2
直到第四个状态a4=1并且后续不会再有状态变化则为符合条件
import java.util.Scanner;
/**
* 7.音节判断
*/
public class _11_07 {
static Scanner sc = new Scanner(System.in);
static String word = sc.next();
public static void main(String[] args) {
String result = core(word);
System.out.println(result);
}
public static String core(String word) {
char[] chars = word.toCharArray();
if(chars.length<4) return "no"; // 单词小于四个字母的不符合
if(isVowel(chars[0])) return "no"; // 第一个是元音字母的不符合
int a1=0,a2=0,a3=0,a4=0; // 0:初始状态 1:当前状态 2:结束状态
for(int i=0;i<chars.length;i++) {
boolean present = isVowel(chars[i]);
if(a1==0 && !present) {a1=1;continue;} // 第一阶段,首个单词为辅音
if(a1==1 && !present) continue; // 第一阶段,多个单词为辅音
if(a1==1 && present) {a1=2;a2=1;continue;} // 第二阶段,首个单词为元音
if(a2==1 && present) continue; // 第二阶段,多个单词为元音
if(a2==1 && !present) {a2=2;a3=1;continue;} // 第三阶段,首个单词为辅音
if(a3==1 && !present) continue; // 第三阶段,多个单词为辅音
if(a3==1 && present) {a3=2;a4=1;continue;} // 第四阶段,首个单词为元音
if(a4==1 && present) continue; // 第四阶段,多个单词为元音
if(a4==1 && !present) return "no"; // 第四个阶段之后出现辅音则不符合
return "no";
}
if(a4==1) return "yes";
return "no";
}
// 判断单词字母是否是元音
public static boolean isVowel(char c) {
if(c=='a' || c=='e' || c=='i' || c=='o' || c=='u') return true;
return false;
}
}
运行结果
ccccccccccccccccccccccccccccoooooooooooooooooooooooooooooobbbbbbbbbbbbbbbbbbbbbbbbbbiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
yes
ccccccccccccccccccccccccccccoooooooooooooooooooooooooooooobbbbbbbbbbbbbbbbbbbbbbbbbbiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiivvvv
no
8.长草
【问题描述】
小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1。
小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。
这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,这四小块空地都将变为有草的小块。
请告诉小明,k 个月后空地上哪些地方有草。
【输入格式】
输入的第一行包含两个整数 n, m。
接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字母之间没有空格。如果为小数点,表示为空地,如果字母为 g,表示种了草。
接下来包含一个整数 k。
【输出格式】
输出 n 行,每行包含 m 个字母,表示 k 个月后空地的状态。如果为小数点,表示为空地,如果字母为 g,表示长了草。
【样例输入】
4 5
.g...
.....
..g..
.....
2
【样例输出】
gggg.
gggg.
ggggg
.ggg.
【评测用例规模与约定】
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= k <= 1000。
import java.util.Arrays;
import java.util.Scanner;
/**
* 8.长草
*/
public class _11_08 {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
static int m = sc.nextInt();
static char[][] chars = new char[n][m];
static {
char[] str;
for(int i=0;i<n;i++) {
str = sc.next().toCharArray();
for(int j=0;j<m;j++) {
chars[i][j] = str[j];
}
}
}
static int k = sc.nextInt();
// static int k = Integer.valueOf(sc.next());
public static void main(String[] args) {
char[][] result = grows(chars,0,k);
output(result);
}
public static char[][] grows(char[][] chars,int start,int k) {
if(start >= k) return chars; // 递归出口,已经长了k个月
char[][] copy = new char[n][m];
// 二维数组拷贝
for (int i=0;i<n;i++) {
copy[i] = Arrays.copyOf(chars[i],chars[i].length);
}
boolean flag = true;
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
if(copy[i][j] == 'g') {
// 四面八方集体长草
if(i-1>=0) chars[i-1][j] = 'g';
if(j-1>=0) chars[i][j-1] = 'g';
if(i+1<n) chars[i+1][j] = 'g';
if(j+1<m) chars[i][j+1] = 'g';
} else flag = false;
}
}
if(flag) return chars; // 如果空地已经全部长草,停止递归
// output(chars); // 查看每次长草的情况
return grows(chars, start+1,k);
}
public static void output(char[][] chars) {
for(int i=0;i<chars.length;i++) {
for(int j=0;j<chars[0].length;j++) {
System.out.print(chars[i][j]);
}System.out.println();
}
}
}
运行结果
4 5
.g...
.....
..g..
.....
2
gggg.
gggg.
ggggg
.ggg.
9.序列计数
【问题描述】
小明想知道,满足以下条件的正整数序列的数量:
1. 第一项为 n;
2. 第二项不超过 n;
3. 从第三项开始,每一项小于前两项的差的绝对值。
请计算,对于给定的 n,有多少种满足条件的序列。
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
【样例输入】
4
【样例输出】
7
【样例说明】
以下是满足条件的序列:
4 1
4 1 1
4 1 2
4 2
4 2 1
4 3
4 4
【评测用例规模与约定】
对于 20% 的评测用例,1 <= n <= 5;
对于 50% 的评测用例,1 <= n <= 10;
对于 80% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000。
方法一
import java.util.HashSet;
import java.util.Scanner;
/**
* 9.序列计数
*/
public class _11_09 {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
static HashSet<String> set = new HashSet<>();
public static void main(String[] args) {
core(n);
foreachSet(set);
System.out.println(set.size());
}
public static void core(int n) {
// 第二个数小于等于n
for(int i=1;i<=n;i++) {
int n1 = n, n2 = i; // 第一列为n,第二列为i
String sequence = n1 + " " + n2;
set.add(sequence); // 初始化序列 n1=n n2<=n
dfs(sequence,n1,n2); // 递归循环
}
}
public static void dfs(String sequence,int n1,int n2) {
if(Math.abs(n1-n2)<=1) return;
for(int next=1;next<Math.abs(n1-n2);next++) {
dfs(put(sequence,next),n2,next);
}
}
public static String put(String sequence,int next) {
sequence += " " + next;
set.add(sequence);
return sequence;
}
public static void foreachSet(HashSet<String> set) {
set.forEach( x -> System.out.println(x));
}
}
虽然可以直观地显示序列,但是很耗费内存
运行结果
5
5 1 3 1 1
5 1 2
5 2 1
5 1 3
5 2 2
5 3 1
5 1
5 2
5 3
5 3 1 1
5 4
5 5
5 1 1
5 1 3 1
14
方法二
import java.util.Scanner;
/**
* 9.序列计数——
*/
public class _11_09_ {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
static int count = 0;
public static void main(String[] args) {
core(n);
System.out.println(count%10000);
}
public static void core(int n) {
// 第二个数小于等于n
for(int i=1;i<=n;i++) {
count++;
dfs(n,i); // 递归循环
}
}
public static void dfs(int n1,int n2) {
if(Math.abs(n1-n2)<=1) return;
for(int next=1;next<Math.abs(n1-n2);next++) {
count++;
dfs(n2,next);
}
}
}
虽然简化了,但是随着数值变大,还是会出现超时和栈溢出的问题。
优化思路一:用树或链表代替递归方法实现递归回溯。
优化思路二:随着数值越大会出现很多重复计算的序列,需要记忆计算过的序列。
运行结果
20
3277
10.晚会节目单
【问题描述】
小明要组织一台晚会,总共准备了 n 个节目。然后晚会的时间有限,他只能最终选择其中的 m 个节目。
这 n 个节目是按照小明设想的顺序给定的,顺序不能改变。
小明发现,观众对于晚上的喜欢程度与前几个节目的好看程度有非常大的关系,他希望选出的第一个节目尽可能好看,在此前提下希望第二个节目尽可能好看,依次类推。
小明给每个节目定义了一个好看值,请你帮助小明选择出 m 个节目,满足他的要求。
【输入格式】
输入的第一行包含两个整数 n, m ,表示节目的数量和要选择的数量。
第二行包含 n 个整数,依次为每个节目的好看值。
【输出格式】
输出一行包含 m 个整数,为选出的节目的好看值。
【样例输入】
5 3
3 1 2 5 4
【样例输出】
3 5 4
【样例说明】
选择了第1, 4, 5个节目。
【评测用例规模与约定】
对于 30% 的评测用例,1 <= n <= 20;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 100000,0 <= 节目的好看值 <= 100000。
错误案例
import java.util.Arrays;
import java.util.Scanner;
/**
* 10.晚会节目单
*/
public class _11_10 {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
static int m = sc.nextInt();
static int[] A = new int[n];
static {
for(int i=0;i<n;i++) {
A[i] = sc.nextInt();
}
}
public static void main(String[] args) {
f();
}
public static void f() {
Arrays.sort(A);
for(int i=n-m;i<n-1;i++) {
System.out.print(A[i]+" ");
}System.out.print(A[n-1]);
}
}
错误结果
100 30
1 5 6 8 3 6 4 7 9 8 2 4 11 5 32 0 15 4 8 7 625 25 13 54 13 5 4 2 5 9 8 7 42 3 6 4 64 84 864 456 46 4 456 45 4564 56 4657 87 41 654 416 574 12 634 896 413 4186 4 16 57 941 6 74 894 1034 13 21 5 64 023 1 657 489 41 30 5 489 40 32 4 10 32 4 610 324 10 32 48 9 64 1 32 48 641 2048 678 10 23 156 41 02 4531 021 1 5 6 8 3 6 4 7 9 8 2 4 11 5 32 0 15 4 8 7 625 25 13 54 13 5 4 2 5 9 8 7 42 3 6 464 84 864 456 46 4 456 45 4564 56 1 5 6 8 3 6 4 7 9 8 2 4 11 5 32 0 15 4 8 7 625 25 13 54 13 5 4 2 5 9 8 7 42 3 6 4 64 84 864 456 46 4 456 45 4564 56 4657 87 41 654 416 574 12 634 896 413 4186 4 16 57 941 6 74 894 1034 13 21 5 64 023 1 657 489 41 30 5 489 40 32 4 10 32 4 610 324 10 32 48 9 64 1 32 48 641 2048 678 10 23 156 41 02 4531 021 1 5 6 8 3 6 4 7 9 8 2 4 11 5 32 0 15 4 8 7 625 25 13 54 13 5 4 2 5 9 8 7 42 3 6 464 84 864 456 46 4 456 45 4564 56
64 64 74 84 87 156 324 413 416 456 456 489 489 574 610 625 634 641 654 657 678 864 894 896 941 1034 2048 4186 4564 4657
正确案例
import java.util.*;
/**
* 10.晚会节目单
* 解题思路:
* 1、如果直接对n个数进行排序,输出的结果不能保证节目的顺序
* 2、使用栈在输入时插入排序,栈存储数组A的索引,取最大的m个值
* 3、最后对数组A的索引重新排序即可还原节目顺序输出前m大的值
*/
public class _11_10_ {
static Scanner sc = new Scanner(System.in);
static int n = sc.nextInt();
static int m = sc.nextInt();
public static void main(String[] args) {
f();
}
public static void f() {
Stack<Integer> stack = new Stack<>();
int[] A = new int[n]; // 存节目好看值
int[] B = new int[m]; // 数组A的索引
A[0]=sc.nextInt();
stack.add(0); // 第一个值直接插入
for(int i=1;i<n;i++) {
A[i] = sc.nextInt();
// 如果当前值大于最后一个则直接添加
if(A[i] < A[stack.firstElement()]) { stack.insertElementAt(i,0); continue; }
if(A[i] > A[stack.lastElement()]) { stack.add(i); continue; }
for(int j=1;j<i;j++) { // 从头遍历栈,直到小于时插入
if(A[i] < A[stack.get(j)]) { stack.insertElementAt(i, j); break; }
}
}
if(n == m) { stack.forEach(i -> System.out.print(A[i]+" ")); return; }
for(int i=n-m,j=0;i<n;i++,j++) {
B[j] = stack.get(i); // 数组B保存A数组的索引
} Arrays.sort(B); // A数组索引从小到大排序
for(int i=0;i<m;i++) System.out.print(A[B[i]]+" "); // 输出结果
}
}
最终结果
10 5
12 74 100 15 54 35 86 63 24 46
74 100 54 86 63