第六届蓝桥杯java B组决赛真题详解
1、分机号
X老板脾气古怪,他们公司的电话分机号都是3位数,老板规定,所有号码必须是降序排列,且不能有重复的数位。比如:
751,520,321 都满足要求,而,
766,918,201 就不符合要求。
现在请你计算一下,按照这样的规定,一共有多少个可用的3位分机号码?
请直接提交该数字,不要填写任何多余的内容。
思路:
这道题的解题方法很简单,根据题目的要求,枚举每一个三位数进行检查就可以解决问题
枚举+检查
public class question1 {
static int ans=0;
public static void main(String args[]) {
int ans=0;
//根据题目要求,三位不重复的数,且依次递减,因为范围很小,我们可以直接枚举在这个区间的每一个数,最小值是210,最大值应该为987
for(int i=210;i<=987;i++) {
int num=i;
int a,b,c;
a=num%10; //第三位数字
b=num/10%10; //第二位数字
c=num/100; //第一位数字
//判断当前分机号的三位数字依次递减且不存在重复数字
if(a<b&&b<c&&a!=c)
ans++;
}
System.out.println(ans);
}
}
最终答案:120
2、五星填数
如【图1.png】的五星图案节点填上数字:1~12,除去7和11。
要求每条直线上数字和相等。
如图就是恰当的填法。
请你利用计算机搜索所有可能的填法有多少种。
注意:旋转或镜像后相同的算同一种填法。
请提交表示方案数目的整数,不要填写任何其它内容。
![]() |
![]() |
思路:解题思路在代码的注释里面
全排列+检查+回溯
/*
解题思路
这道题可以变成对1,2,3,4,5,6,8,9,10,12几个数字进行全排列
然后检查每种排列是否满足要求,满足要求就将填发增加1
因为旋转或镜像后相同的算作同一种方法,可以每种填发可以经过旋转可以有5种形式,经过镜像之后有5中形式
将最后算的的结果除以10就是最终的答案
*/
public class question2 {
static int count=0;
public static void main(String args[]) {
int[] a= {1,2,3,4,5,6,8,9,10,12};
f(a,0,a.length-1);
System.out.println("ans="+count/10); //满足条件的全排列的数量除以10,就是本题的结果
}
private static void f(int[] a, int start, int end) {
if(start==end) { //全排列完成,检查排列的结果,如果满足要求就将解法加一
if(check(a))
count++;
}
//将当前位后面的每一位都和当前的这一位数字发生交换,完成全排列
for (int i = start; i <= end; i++) {
swap(a,i,start); //交换
f(a, start+1, end);
swap(a,i,start); //回溯,将这次循环中的交换恢复,保证数组的内容不发生变化
}
}
//检查排列结果,即对每一条边上的值求和,判断各条边是否相等
private static boolean check(int[] a) {
int x=a[1]+a[4]+a[5]+a[6];
int y=a[1]+a[3]+a[7]+a[8];
int z=a[2]+a[4]+a[8]+a[9];
int h=a[0]+a[3]+a[5]+a[9];
int m=a[0]+a[2]+a[6]+a[7];
if(x==y&&x==z&&x==h&&x==m)
return true;
return false;
}
//交换数组中的两个指定下标的元素
private static void swap(int[] a, int i, int start) {
int b=a[i];
a[i]=a[start];
a[start]=b;
}
}
最终答案:12
3、显示二叉树(代码填空)
题目
标题:显示二叉树
排序二叉树的特征是:
某个节点的左子树的所有节点值都不大于本节点值。
某个节点的右子树的所有节点值都不小于本节点值。
为了能形象地观察二叉树的建立过程,小明写了一段程序来显示出二叉树的结构来。
class BiTree
{
private int v;
private BiTree l;
private BiTree r;
public BiTree(int v){
this.v = v;
}
public void add(BiTree the){
if(the.v < v){
if(l==null) l = the;
else l.add(the);
}
else{
if(r==null) r = the;
else r.add(the);
}
}
public int getHeight(){
int h = 2;
int hl = l==null? 0 : l.getHeight();
int hr = r==null? 0 : r.getHeight();
return h + Math.max(hl,hr);
}
public int getWidth(){
int w = (""+v).length();
if(l!=null) w += l.getWidth();
if(r!=null) w += r.getWidth();
return w;
}
public void show(){
char[][] buf = new char[getHeight()][getWidth()];
printInBuf(buf, 0, 0);
showBuf(buf);
}
private void showBuf(char[][] x){
for(int i=0; i<x.length; i++){
for(int j=0; j<x[i].length; j++)
System.out.print(x[i][j]==0? ' ':x[i][j]);
System.out.println();
}
}
private void printInBuf(char[][] buf, int x, int y){
String sv = "" + v;
int p1 = l==null? x : l.getRootPos(x);
int p2 = getRootPos(x);
int p3 = r==null? p2 : r.getRootPos(p2+sv.length());
buf[y][p2] = '|';
for(int i=p1; i<=p3; i++) buf[y+1][i]='-';
for(int i=0; i<sv.length(); i++) ________________________________; //填空位置
if(p1<p2) buf[y+1][p1] = '/';
if(p3>p2) buf[y+1][p3] = '\\';
if(l!=null) l.printInBuf(buf,x,y+2);
if(r!=null) r.printInBuf(buf,p2+sv.length(),y+2);
}
private int getRootPos(int x){
return l==null? x : x + l.getWidth();
}
}
public class Main
{
public static void main(String[] args)
{
BiTree tree = new BiTree(500);
tree.add(new BiTree(200));
tree.add(new BiTree(509));
tree.add(new BiTree(100));
tree.add(new BiTree(250));
tree.add(new BiTree(507));
tree.add(new BiTree(600));
tree.add(new BiTree(650));
tree.add(new BiTree(450));
tree.add(new BiTree(510));
tree.add(new BiTree(440));
tree.add(new BiTree(220));
tree.show();
}
}
对于上边的测试数据,应该显示出:
|
/--------------500---\
| |
/--200---\ /--509---\
| | | |
100 /--250---\ 507 /--600\
| | | |
220 /--450 510 650
|
440
(如有对齐问题,请参考【图1.png】)
请分析程序逻辑,填写划线部分缺失的代码。
注意,只填写缺少的部分,不要填写已有的代码或符号,也不要加任何说明文字。
思路
对于这种代码填空的题目,完全没有必要完全看懂代码,既然只让写一行代码,那就肯定是关键的语句,可以使用Debug的方式来解决这种题目,先把要填写代码的行注释掉,然后运行这段代码,观察实际输出的结果和题目又什么差别,然后再去填写代码,基本上要填写的哪一行里就是在程序中定义过却从未使用的变量,或者是比较关键的变量,掌握这种思路求解这类问题就会很简单。
最终答案
buf[y+1][i+p2]=sv.toCharArray()[i];
4、穿越雷区
X星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区才能保持正常运转,否则将报废。
某坦克需要从A区到B区去(A,B区本身是安全区,没有正能量或负能量特征),怎样走才能路径最短?
已知的地图是一个方阵,上面用字母标出了A,B区,其它区都标了正号或负号分别表示正负能量辐射区。
例如:
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
坦克车只能水平或垂直方向上移动到相邻的区。
数据格式要求:
输入第一行是一个整数n,表示方阵的大小, 4<=n<100
接下来是n行,每行有n个数据,可能是A,B,+,-中的某一个,中间用空格分开。
A,B都只出现一次。
要求输出一个整数,表示坦克从A区到B区的最少移动步数。
如果没有方案,则输出-1
例如:
用户输入:
5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
则程序应该输出:
10
资源约定:
峰值内存消耗(含虚拟机) < 512M
CPU消耗 < 2000ms
思路
基本思路:
求解在图中的最短路径问题,用到的算法大多数都是BFS(广度优先搜索),因为是广度优先搜索,所以找到的结果就是最短路径。
明确了要用到的算法,接下来问题就会变得简单很多,无非就是节点能不能走得通的判断
从起始位置进行BFS,搜索四周的几个节点,如果节点在地图范围内且可以行走(可以行走的条件是下一个节点的值和本节点不同,即题目所说的坦克必须交替穿越正能量辐射区和负能量辐射区),则在下一步走到四周的某一个可以走得通的节点,并将路径数目增加1,一直到找到B节点退出BFS,路径距离即为所求。
代码思路:
首先定义一个节点的类Node,用来存储每一个可以行走的节点,接着声明一个队列Queue,用来存储可以走的通的节点,不停执行入队和出队操作,直到当前队列为空(走不通)时结束,返回-1,或者走到B节点,返回路径长度。
因为行走过程中可能会重复走过一个节点,为了优化代码,降低时间复杂度,可以设置一个二维的布尔数组,用来标记一个节点是否被走过,若走过则不在对该节点执行入队操作
知识要点:
队列,BFS,字符串处理,节点判断,代码优化
代码
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class question4 {
//字符数组,用来存储地图
static char[][] array;
static int n;
public static void main(String args[]) {
Scanner in=new Scanner(System.in);
//对用户输入进行处理,此处不做解析
n=in.nextInt();
array=new char[n][n];
String s="";
in.nextLine();
for (int i = 0; i < n; i++) {
s=in.nextLine().replaceAll(" ", "");
for (int j = 0; j < n; j++) {
array[i][j]= s.toCharArray()[j];
}
}
int x=0,y=0;
//查找A节点,即起始位置,从该处开始执行BFS
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if(array[i][j]=='A') {
x=i;y=j;
}
}
if(x!=0)
break;
}
//实例化一个节点,从此处开始查找
Node node=new Node(x, y, 'A', 0);
System.out.println(f(node));
}
private static int f(Node node) {
Queue<Node> q=new LinkedList<Node>(); //声明一个队列,存储当前可以走的通的节点
q.add(node); //起始节点入队
boolean[][] flag=new boolean[n][n]; //标志数组,标记每一个节点是否被走过
int[] x= {1,0,-1,0};
int[] y= {0,1,0,-1};
while (!q.isEmpty()) { //如果队列非空
Node now=q.peek(); //取出队头节点
flag[now.x][now.y]=true; //标记该节点已经被访问过了
q.remove(); //移除已经访问过的节点
for(int i=0;i<4;i++) { //遍历当前节点的四周节点
//遍历当前节点的四周的每个节点
//得到当前节点的下一个节点的位置和属性
int next_x=now.x+x[i];
int next_y=now.y+y[i];
if(next_x<0||next_x>=n||next_y<0||next_y>=n) //如果这个节点不在地图范围内,直接退出,寻找下一个相邻节点
continue;
char next_value=array[next_x][next_y];
int next_step=now.step+1;
if(next_value=='B') //如果这个节点是B,返回路径长度
return next_step;
if(now.value!=next_value&&flag[next_x][next_y]==false) //当前节点和下一个节点不同(交替穿越正负能量区),且下一个节点没有没有访问过,将其加入队尾
{
Node next=new Node(next_x, next_y, next_value, next_step);
q.add(next);
}
}
}
//如果走不通,返回-1
return -1;
}
}
//节点类,标记图中的每一个节点
class Node{
int x; //节点的x坐标
int y; //节点的y坐标
char value; //节点的值
int step; //到达该节点的最短路径长度
public Node(int x, int y, char value, int step) {
super();
this.x = x;
this.y = y;
this.value = value;
this.step = step;
}
}
5、表格计算
某次无聊中, atm 发现了一个很老的程序。这个程序的功能类似于 Excel ,它对一个表格进行操作。
不妨设表格有 n 行,每行有 m 个格子。
每个格子的内容可以是一个正整数,也可以是一个公式。
公式包括三种:
- SUM(x1,y1:x2,y2) 表示求左上角是第 x1 行第 y1 个格子,右下角是第 x2 行第 y2 个格子这个矩形内所有格子的值的和。
- AVG(x1,y1:x2,y2) 表示求左上角是第 x1 行第 y1 个格子,右下角是第 x2 行第 y2 个格子这个矩形内所有格子的值的平均数。
- STD(x1,y1:x2,y2) 表示求左上角是第 x1 行第 y1 个格子,右下角是第 x2 行第 y2 个格子这个矩形内所有格子的值的标准差。
标准差即为方差的平方根。
方差就是:每个数据与平均值的差的平方的平均值,用来衡量单个数据离开平均数的程度。
公式都不会出现嵌套。
如果这个格子内是一个数,则这个格子的值等于这个数,否则这个格子的值等于格子公式求值结果。
输入这个表格后,程序会输出每个格子的值。atm 觉得这个程序很好玩,他也想实现一下这个程序。
「输入格式」
第一行两个数 n, m 。
接下来 n 行输入一个表格。每行 m 个由空格隔开的字符串,分别表示对应格子的内容。
输入保证不会出现循环依赖的情况,即不会出现两个格子 a 和 b 使得 a 的值依赖 b 的值且 b 的值依赖 a 的值。
「输出格式」
输出一个表格,共 n 行,每行 m 个保留两位小数的实数。
数据保证不会有格子的值超过 1e6 。
「样例输入」
3 2
1 SUM(2,1:3,1)
2 AVG(1,1:1,2)
SUM(1,1:2,1) STD(1,1:2,2)
「样例输出」
1.00 5.00
2.00 3.00
3.00 1.48
「数据范围」
对于 30% 的数据,满足: n, m <= 5
对于 100% 的数据,满足: n, m <= 50
资源约定:
峰值内存消耗(含虚拟机) < 512M
CPU消耗 < 2000ms
思路:
递归求解每一个公式的结果。
声明一个double的二维数组存放格子的数字,声明一个String二维数组存放用书输入每一个格子的内容,声明一个boolean二维数标记每一个格子是公式还是数值。
对于是公式的格子,根据公式类型调用对应的函数求解,因为平均值和方差都需要用到求和公式,所以只需在求和函数sum()里面进行递归操作,详细操作参考程序代码,本题不做详细解释。
本题需要定义5个方法
static void f(int i, int j) //求解一个公式的格子,将结果赋给存放数值的数组,并将格子标记为是一个数字
double sum(int x1,int y1,int x2,int y2) //求解(x1,y1,x2,y2)范围内的和
static double avg(int x1,int y1,int x2,int y2) //求解(x1,y1,x2,y2)范围内的平均数
double std(int x1,int y1,int x2,int y2) //求解(x1,y1,x2,y2)范围内的标准差
boolean check(String s) //检查一个字符串是不是公式,如果是返回true,不是返回false
源码
import java.io.StreamCorruptedException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class question5 {
static int n,m;
static String[][] str;
static double[][] array;
static boolean[][] flag;
public static void main(String args[]) {
//首先解决输入问题
Scanner in=new Scanner(System.in);
n=in.nextInt();
m=in.nextInt();
str=new String[n][m];
array=new double[n][m];
flag=new boolean[n][m];
in.nextLine(); //将剩余的空行读出,开始接收数据
for (int i = 0; i < n; i++) {
String[] s=in.nextLine().split(" ");
for (int j = 0; j < m; j++) {
str[i][j]=s[j];
}
}
//输入问题已解决,开始进行数据的转换,首先进行初始数据的转换,就是用户刚开始输入的内容
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if(check(str[i][j]))
continue;
flag[i][j]=true;
array[i][j]=Double.parseDouble(str[i][j]);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if(!flag[i][j]) { //如果当前的这个格子不是数字,进行转换
f(i,j);
}
}
}
//数据转换已完成,开始向外输出数据
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
System.out.printf("%.2f\t",array[i][j]);
}
System.out.println();
}
}
private static void f(int i, int j) {
double ans=0;
String[] value=((String) str[i][j].subSequence(4, str[i][j].length()-1)).split(":"); //将里面的四个值提取出来
int x1=Integer.parseInt(value[0].split(",")[0]);
int y1=Integer.parseInt(value[0].split(",")[1]);
int x2=Integer.parseInt(value[1].split(",")[0]);
int y2=Integer.parseInt(value[1].split(",")[1]);
if(((String)str[i][j].subSequence(0, 3)).equals("SUM"))
ans=sum(x1,y1,x2,y2);
if(((String)str[i][j].subSequence(0, 3)).equals("AVG"))
ans=avg(x1,y1,x2,y2);
if(((String)str[i][j].subSequence(0, 3)).equals("STD"))
ans=std(x1,y1,x2,y2);
array[i][j]=ans;
flag[i][j]=true;
}
//这三个函数是三个公式
static double sum(int x1,int y1,int x2,int y2) {
double sum=0;
for (int i = x1-1; i < x2; i++) {
for (int j = y1-1; j < y2; j++) {
if(!flag[i][j])
f(i, j);
sum+=array[i][j];
}
}
return sum;
}
static double avg(int x1,int y1,int x2,int y2) {
double sum=0;
sum=sum(x1, y1, x2, y2);
int count=(x2-x1+1)*(y2-y1+1);
sum=sum/count;
return sum;
}
static double std(int x1,int y1,int x2,int y2) {
double avg=avg(x1, y1, x2, y2);
double sum=0;
int count=(x2-x1+1)*(y2-y1+1);
for (int i = x1-1; i < x2; i++) {
for (int j = y1-1; j < y2; j++) {
sum+=Math.pow((array[i][j]-avg), 2);
}
}
sum=sum/count; //方差
sum=Math.sqrt(sum); //标准差
return sum;
}
//检查一个字符串是不是公式,如果是返回true,不是返回false
static boolean check(String s) {
char c=s.toCharArray()[0];
if(c>60)
return true;
else
return false;
}
}
/*
样例输入
3 2
1 SUM(2,1:3,1)
2 AVG(1,1:1,2)
SUM(1,1:2,1) STD(1,1:2,2)
样例输出
1.00 5.00
2.00 3.00
3.00 1.48
*/